Blame view

kernel/profile.c 14.7 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
  /*
   *  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,
6d49e352a   Nadia Yvette Chambers   propagate name ch...
11
   *	Nadia Yvette Chambers, Oracle, July 2004
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
12
   *  Amortized hit count accounting via per-cpu open-addressed hashtables
6d49e352a   Nadia Yvette Chambers   propagate name ch...
13
14
   *	to resolve timer interrupt livelocks, Nadia Yvette Chambers,
   *	Oracle, 2004
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
15
   */
9984de1a5   Paul Gortmaker   kernel: Map most ...
16
  #include <linux/export.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
17
18
19
20
21
22
  #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
23
  #include <linux/highmem.h>
97d1f15b7   Arjan van de Ven   [PATCH] sem2mutex...
24
  #include <linux/mutex.h>
22b8ce947   Dave Hansen   profiling: dynami...
25
26
  #include <linux/slab.h>
  #include <linux/vmalloc.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
27
  #include <asm/sections.h>
7d12e780e   David Howells   IRQ: Maintain reg...
28
  #include <asm/irq_regs.h>
e8edc6e03   Alexey Dobriyan   Detach sched.h fr...
29
  #include <asm/ptrace.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
30
31
32
33
34
35
36
37
  
  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)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
38
39
  static atomic_t *prof_buffer;
  static unsigned long prof_len, prof_shift;
07031e14c   Ingo Molnar   [PATCH] KVM: add ...
40

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

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

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

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

1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
153
  int task_handoff_unregister(struct notifier_block *n)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
154
  {
e041c6834   Alan Stern   [PATCH] Notifier ...
155
  	return atomic_notifier_chain_unregister(&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_unregister);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
158

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

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
163
  	switch (type) {
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
164
165
166
167
168
169
170
171
  	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
172
  	}
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
173

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
174
175
  	return err;
  }
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
176
  EXPORT_SYMBOL_GPL(profile_event_register);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
177

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

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
182
  	switch (type) {
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
183
184
185
186
187
188
189
190
  	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
191
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
192
193
  	return err;
  }
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
194
  EXPORT_SYMBOL_GPL(profile_event_unregister);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
195

ade356b99   Arnd Bergmann   profile: hide unu...
196
  #if defined(CONFIG_SMP) && defined(CONFIG_PROC_FS)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
  /*
   * 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...
219
220
   * SCHED_PROFILING or SLEEP_PROFILING profile_hit() may be called from
   * process context).
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
221
222
223
224
225
   * 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.
   *
6d49e352a   Nadia Yvette Chambers   propagate name ch...
226
   * -- nyc
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
227
228
229
230
231
232
233
234
235
236
237
   */
  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...
238
  	mutex_lock(&profile_flip_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
239
240
  	j = per_cpu(cpu_profile_flip, get_cpu());
  	put_cpu();
15c8b6c1a   Jens Axboe   on_each_cpu(): ki...
241
  	on_each_cpu(__profile_flip_buffers, NULL, 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
242
243
244
245
246
247
248
249
250
251
252
253
  	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...
254
  	mutex_unlock(&profile_flip_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
255
256
257
258
259
  }
  
  static void profile_discard_flip_buffers(void)
  {
  	int i, cpu;
97d1f15b7   Arjan van de Ven   [PATCH] sem2mutex...
260
  	mutex_lock(&profile_flip_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
261
262
  	i = per_cpu(cpu_profile_flip, get_cpu());
  	put_cpu();
15c8b6c1a   Jens Axboe   on_each_cpu(): ki...
263
  	on_each_cpu(__profile_flip_buffers, NULL, 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
264
265
266
267
  	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...
268
  	mutex_unlock(&profile_flip_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
269
  }
6f7bd76f0   Rakib Mullick   kernel/profile.c:...
270
  static void do_profile_hits(int type, void *__pc, unsigned int nr_hits)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
271
272
273
274
  {
  	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
275
276
277
278
279
280
281
282
283
  	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...
284
285
286
287
288
  	/*
  	 * 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
289
290
291
292
  	local_irq_save(flags);
  	do {
  		for (j = 0; j < PROFILE_GRPSZ; ++j) {
  			if (hits[i + j].pc == pc) {
ece8a684c   Ingo Molnar   [PATCH] sleep pro...
293
  				hits[i + j].hits += nr_hits;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
294
295
296
  				goto out;
  			} else if (!hits[i + j].hits) {
  				hits[i + j].pc = pc;
ece8a684c   Ingo Molnar   [PATCH] sleep pro...
297
  				hits[i + j].hits = nr_hits;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
298
299
300
301
302
  				goto out;
  			}
  		}
  		i = (i + secondary) & (NR_PROFILE_HIT - 1);
  	} while (i != primary);
ece8a684c   Ingo Molnar   [PATCH] sleep pro...
303
304
305
306
307
308
  
  	/*
  	 * 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
309
310
311
312
313
314
315
316
  	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();
  }
e722d8daa   Sebastian Andrzej Siewior   profile: Convert ...
317
  static int profile_dead_cpu(unsigned int cpu)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
318
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
319
  	struct page *page;
e722d8daa   Sebastian Andrzej Siewior   profile: Convert ...
320
  	int i;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
321

e722d8daa   Sebastian Andrzej Siewior   profile: Convert ...
322
323
324
325
326
327
328
  	if (prof_cpu_mask != NULL)
  		cpumask_clear_cpu(cpu, prof_cpu_mask);
  
  	for (i = 0; i < 2; i++) {
  		if (per_cpu(cpu_profile_hits, cpu)[i]) {
  			page = virt_to_page(per_cpu(cpu_profile_hits, cpu)[i]);
  			per_cpu(cpu_profile_hits, cpu)[i] = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
329
330
  			__free_page(page);
  		}
e722d8daa   Sebastian Andrzej Siewior   profile: Convert ...
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
  	}
  	return 0;
  }
  
  static int profile_prepare_cpu(unsigned int cpu)
  {
  	int i, node = cpu_to_mem(cpu);
  	struct page *page;
  
  	per_cpu(cpu_profile_flip, cpu) = 0;
  
  	for (i = 0; i < 2; i++) {
  		if (per_cpu(cpu_profile_hits, cpu)[i])
  			continue;
  
  		page = __alloc_pages_node(node, GFP_KERNEL | __GFP_ZERO, 0);
  		if (!page) {
  			profile_dead_cpu(cpu);
  			return -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
350
  		}
e722d8daa   Sebastian Andrzej Siewior   profile: Convert ...
351
  		per_cpu(cpu_profile_hits, cpu)[i] = page_address(page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
352
  	}
e722d8daa   Sebastian Andrzej Siewior   profile: Convert ...
353
354
355
356
357
358
359
360
361
  	return 0;
  }
  
  static int profile_online_cpu(unsigned int cpu)
  {
  	if (prof_cpu_mask != NULL)
  		cpumask_set_cpu(cpu, prof_cpu_mask);
  
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
362
  }
e722d8daa   Sebastian Andrzej Siewior   profile: Convert ...
363

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
364
365
366
  #else /* !CONFIG_SMP */
  #define profile_flip_buffers()		do { } while (0)
  #define profile_discard_flip_buffers()	do { } while (0)
6f7bd76f0   Rakib Mullick   kernel/profile.c:...
367
  static void do_profile_hits(int type, void *__pc, unsigned int nr_hits)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
368
369
  {
  	unsigned long pc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
370
  	pc = ((unsigned long)__pc - (unsigned long)_stext) >> prof_shift;
ece8a684c   Ingo Molnar   [PATCH] sleep pro...
371
  	atomic_add(nr_hits, &prof_buffer[min(pc, prof_len - 1)]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
372
373
  }
  #endif /* !CONFIG_SMP */
6f7bd76f0   Rakib Mullick   kernel/profile.c:...
374
375
376
377
378
379
380
  
  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:...
381
  EXPORT_SYMBOL_GPL(profile_hits);
7d12e780e   David Howells   IRQ: Maintain reg...
382
  void profile_tick(int type)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
383
  {
7d12e780e   David Howells   IRQ: Maintain reg...
384
  	struct pt_regs *regs = get_irq_regs();
c309b917c   Rusty Russell   cpumask: convert ...
385
386
  	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
387
388
389
390
391
  		profile_hit(type, (void *)profile_pc(regs));
  }
  
  #ifdef CONFIG_PROC_FS
  #include <linux/proc_fs.h>
583a22e7c   Alexey Dobriyan   kernel/profile.c:...
392
  #include <linux/seq_file.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
393
  #include <asm/uaccess.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
394

583a22e7c   Alexey Dobriyan   kernel/profile.c:...
395
  static int prof_cpu_mask_proc_show(struct seq_file *m, void *v)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
396
  {
ccbd59c1c   Tejun Heo   profile: use %*pb...
397
398
  	seq_printf(m, "%*pb
  ", cpumask_pr_args(prof_cpu_mask));
583a22e7c   Alexey Dobriyan   kernel/profile.c:...
399
400
401
402
403
404
  	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
405
  }
583a22e7c   Alexey Dobriyan   kernel/profile.c:...
406
407
  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
408
  {
c309b917c   Rusty Russell   cpumask: convert ...
409
  	cpumask_var_t new_value;
583a22e7c   Alexey Dobriyan   kernel/profile.c:...
410
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
411

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

c309b917c   Rusty Russell   cpumask: convert ...
415
416
  	err = cpumask_parse_user(buffer, count, new_value);
  	if (!err) {
583a22e7c   Alexey Dobriyan   kernel/profile.c:...
417
418
  		cpumask_copy(prof_cpu_mask, new_value);
  		err = count;
c309b917c   Rusty Russell   cpumask: convert ...
419
420
421
  	}
  	free_cpumask_var(new_value);
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
422
  }
583a22e7c   Alexey Dobriyan   kernel/profile.c:...
423
424
425
426
427
428
429
  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,
  };
fbd387aea   Al Viro   create_proc_cpu_m...
430
  void create_prof_cpu_mask(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
431
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
432
  	/* create /proc/irq/prof_cpu_mask */
fbd387aea   Al Viro   create_proc_cpu_m...
433
  	proc_create("irq/prof_cpu_mask", 0600, NULL, &prof_cpu_mask_proc_fops);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
434
435
436
437
438
439
440
441
442
443
444
445
446
  }
  
  /*
   * 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...
447
  	char *pnt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
448
449
450
451
452
453
454
455
456
457
  	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...
458
  		if (put_user(*((char *)(&sample_step)+p), buf))
064b022c7   Heiko Carstens   [PATCH] profile: ...
459
  			return -EFAULT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
460
461
462
  		buf++; p++; count--; read++;
  	}
  	pnt = (char *)prof_buffer + p - sizeof(atomic_t);
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
463
  	if (copy_to_user(buf, (void *)pnt, count))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
  		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...
480
  	extern int setup_profiling_timer(unsigned int multiplier);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
  
  	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...
496
  static const struct file_operations proc_profile_operations = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
497
498
  	.read		= read_profile,
  	.write		= write_profile,
6038f373a   Arnd Bergmann   llseek: automatic...
499
  	.llseek		= default_llseek,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
500
  };
e722d8daa   Sebastian Andrzej Siewior   profile: Convert ...
501
  int __ref create_proc_profile(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
502
  {
e722d8daa   Sebastian Andrzej Siewior   profile: Convert ...
503
504
505
  	struct proc_dir_entry *entry;
  #ifdef CONFIG_SMP
  	enum cpuhp_state online_state;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
506
  #endif
c270a8171   Srivatsa S. Bhat   profile: Fix CPU ...
507
  	int err = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
508
509
510
  
  	if (!prof_on)
  		return 0;
e722d8daa   Sebastian Andrzej Siewior   profile: Convert ...
511
512
513
514
515
516
517
518
519
520
521
522
523
  #ifdef CONFIG_SMP
  	err = cpuhp_setup_state(CPUHP_PROFILE_PREPARE, "PROFILE_PREPARE",
  				profile_prepare_cpu, profile_dead_cpu);
  	if (err)
  		return err;
  
  	err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "AP_PROFILE_ONLINE",
  				profile_online_cpu, NULL);
  	if (err < 0)
  		goto err_state_prep;
  	online_state = err;
  	err = 0;
  #endif
c33fff0af   Denis V. Lunev   kernel: use non-r...
524
525
  	entry = proc_create("profile", S_IWUSR | S_IRUGO,
  			    NULL, &proc_profile_operations);
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
526
  	if (!entry)
e722d8daa   Sebastian Andrzej Siewior   profile: Convert ...
527
  		goto err_state_onl;
271a15eab   David Howells   proc: Supply PDE ...
528
  	proc_set_size(entry, (1 + prof_len) * sizeof(atomic_t));
c270a8171   Srivatsa S. Bhat   profile: Fix CPU ...
529

e722d8daa   Sebastian Andrzej Siewior   profile: Convert ...
530
531
532
533
534
535
536
  	return err;
  err_state_onl:
  #ifdef CONFIG_SMP
  	cpuhp_remove_state(online_state);
  err_state_prep:
  	cpuhp_remove_state(CPUHP_PROFILE_PREPARE);
  #endif
c270a8171   Srivatsa S. Bhat   profile: Fix CPU ...
537
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
538
  }
c96d6660d   Paul Gortmaker   kernel: audit/fix...
539
  subsys_initcall(create_proc_profile);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
540
  #endif /* CONFIG_PROC_FS */