Blame view

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

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

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

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

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

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

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

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

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

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

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

e722d8daa   Sebastian Andrzej Siewior   profile: Convert ...
323
324
325
326
327
328
329
  	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
330
331
  			__free_page(page);
  		}
e722d8daa   Sebastian Andrzej Siewior   profile: Convert ...
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
  	}
  	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
351
  		}
e722d8daa   Sebastian Andrzej Siewior   profile: Convert ...
352
  		per_cpu(cpu_profile_hits, cpu)[i] = page_address(page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
353
  	}
e722d8daa   Sebastian Andrzej Siewior   profile: Convert ...
354
355
356
357
358
359
360
361
362
  	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
363
  }
e722d8daa   Sebastian Andrzej Siewior   profile: Convert ...
364

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

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

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

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

e722d8daa   Sebastian Andrzej Siewior   profile: Convert ...
531
532
533
534
535
536
537
  	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 ...
538
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
539
  }
c96d6660d   Paul Gortmaker   kernel: audit/fix...
540
  subsys_initcall(create_proc_profile);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
541
  #endif /* CONFIG_PROC_FS */