1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18#include <linux/export.h>
19#include <linux/profile.h>
20#include <linux/memblock.h>
21#include <linux/notifier.h>
22#include <linux/mm.h>
23#include <linux/cpumask.h>
24#include <linux/cpu.h>
25#include <linux/highmem.h>
26#include <linux/mutex.h>
27#include <linux/slab.h>
28#include <linux/vmalloc.h>
29#include <linux/sched/stat.h>
30
31#include <asm/sections.h>
32#include <asm/irq_regs.h>
33#include <asm/ptrace.h>
34
35struct profile_hit {
36 u32 pc, hits;
37};
38#define PROFILE_GRPSHIFT 3
39#define PROFILE_GRPSZ (1 << PROFILE_GRPSHIFT)
40#define NR_PROFILE_HIT (PAGE_SIZE/sizeof(struct profile_hit))
41#define NR_PROFILE_GRP (NR_PROFILE_HIT/PROFILE_GRPSZ)
42
43static atomic_t *prof_buffer;
44static unsigned long prof_len;
45static unsigned short int prof_shift;
46
47int prof_on __read_mostly;
48EXPORT_SYMBOL_GPL(prof_on);
49
50static cpumask_var_t prof_cpu_mask;
51#if defined(CONFIG_SMP) && defined(CONFIG_PROC_FS)
52static DEFINE_PER_CPU(struct profile_hit *[2], cpu_profile_hits);
53static DEFINE_PER_CPU(int, cpu_profile_flip);
54static DEFINE_MUTEX(profile_flip_mutex);
55#endif
56
57int profile_setup(char *str)
58{
59 static const char schedstr[] = "schedule";
60 static const char sleepstr[] = "sleep";
61 static const char kvmstr[] = "kvm";
62 int par;
63
64 if (!strncmp(str, sleepstr, strlen(sleepstr))) {
65#ifdef CONFIG_SCHEDSTATS
66 force_schedstat_enabled();
67 prof_on = SLEEP_PROFILING;
68 if (str[strlen(sleepstr)] == ',')
69 str += strlen(sleepstr) + 1;
70 if (get_option(&str, &par))
71 prof_shift = clamp(par, 0, BITS_PER_LONG - 1);
72 pr_info("kernel sleep profiling enabled (shift: %u)\n",
73 prof_shift);
74#else
75 pr_warn("kernel sleep profiling requires CONFIG_SCHEDSTATS\n");
76#endif
77 } else if (!strncmp(str, schedstr, strlen(schedstr))) {
78 prof_on = SCHED_PROFILING;
79 if (str[strlen(schedstr)] == ',')
80 str += strlen(schedstr) + 1;
81 if (get_option(&str, &par))
82 prof_shift = clamp(par, 0, BITS_PER_LONG - 1);
83 pr_info("kernel schedule profiling enabled (shift: %u)\n",
84 prof_shift);
85 } else if (!strncmp(str, kvmstr, strlen(kvmstr))) {
86 prof_on = KVM_PROFILING;
87 if (str[strlen(kvmstr)] == ',')
88 str += strlen(kvmstr) + 1;
89 if (get_option(&str, &par))
90 prof_shift = clamp(par, 0, BITS_PER_LONG - 1);
91 pr_info("kernel KVM profiling enabled (shift: %u)\n",
92 prof_shift);
93 } else if (get_option(&str, &par)) {
94 prof_shift = clamp(par, 0, BITS_PER_LONG - 1);
95 prof_on = CPU_PROFILING;
96 pr_info("kernel profiling enabled (shift: %u)\n",
97 prof_shift);
98 }
99 return 1;
100}
101__setup("profile=", profile_setup);
102
103
104int __ref profile_init(void)
105{
106 int buffer_bytes;
107 if (!prof_on)
108 return 0;
109
110
111 prof_len = (_etext - _stext) >> prof_shift;
112 buffer_bytes = prof_len*sizeof(atomic_t);
113
114 if (!alloc_cpumask_var(&prof_cpu_mask, GFP_KERNEL))
115 return -ENOMEM;
116
117 cpumask_copy(prof_cpu_mask, cpu_possible_mask);
118
119 prof_buffer = kzalloc(buffer_bytes, GFP_KERNEL|__GFP_NOWARN);
120 if (prof_buffer)
121 return 0;
122
123 prof_buffer = alloc_pages_exact(buffer_bytes,
124 GFP_KERNEL|__GFP_ZERO|__GFP_NOWARN);
125 if (prof_buffer)
126 return 0;
127
128 prof_buffer = vzalloc(buffer_bytes);
129 if (prof_buffer)
130 return 0;
131
132 free_cpumask_var(prof_cpu_mask);
133 return -ENOMEM;
134}
135
136
137
138static BLOCKING_NOTIFIER_HEAD(task_exit_notifier);
139static ATOMIC_NOTIFIER_HEAD(task_free_notifier);
140static BLOCKING_NOTIFIER_HEAD(munmap_notifier);
141
142void profile_task_exit(struct task_struct *task)
143{
144 blocking_notifier_call_chain(&task_exit_notifier, 0, task);
145}
146
147int profile_handoff_task(struct task_struct *task)
148{
149 int ret;
150 ret = atomic_notifier_call_chain(&task_free_notifier, 0, task);
151 return (ret == NOTIFY_OK) ? 1 : 0;
152}
153
154void profile_munmap(unsigned long addr)
155{
156 blocking_notifier_call_chain(&munmap_notifier, 0, (void *)addr);
157}
158
159int task_handoff_register(struct notifier_block *n)
160{
161 return atomic_notifier_chain_register(&task_free_notifier, n);
162}
163EXPORT_SYMBOL_GPL(task_handoff_register);
164
165int task_handoff_unregister(struct notifier_block *n)
166{
167 return atomic_notifier_chain_unregister(&task_free_notifier, n);
168}
169EXPORT_SYMBOL_GPL(task_handoff_unregister);
170
171int profile_event_register(enum profile_type type, struct notifier_block *n)
172{
173 int err = -EINVAL;
174
175 switch (type) {
176 case PROFILE_TASK_EXIT:
177 err = blocking_notifier_chain_register(
178 &task_exit_notifier, n);
179 break;
180 case PROFILE_MUNMAP:
181 err = blocking_notifier_chain_register(
182 &munmap_notifier, n);
183 break;
184 }
185
186 return err;
187}
188EXPORT_SYMBOL_GPL(profile_event_register);
189
190int profile_event_unregister(enum profile_type type, struct notifier_block *n)
191{
192 int err = -EINVAL;
193
194 switch (type) {
195 case PROFILE_TASK_EXIT:
196 err = blocking_notifier_chain_unregister(
197 &task_exit_notifier, n);
198 break;
199 case PROFILE_MUNMAP:
200 err = blocking_notifier_chain_unregister(
201 &munmap_notifier, n);
202 break;
203 }
204
205 return err;
206}
207EXPORT_SYMBOL_GPL(profile_event_unregister);
208
209#if defined(CONFIG_SMP) && defined(CONFIG_PROC_FS)
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241static void __profile_flip_buffers(void *unused)
242{
243 int cpu = smp_processor_id();
244
245 per_cpu(cpu_profile_flip, cpu) = !per_cpu(cpu_profile_flip, cpu);
246}
247
248static void profile_flip_buffers(void)
249{
250 int i, j, cpu;
251
252 mutex_lock(&profile_flip_mutex);
253 j = per_cpu(cpu_profile_flip, get_cpu());
254 put_cpu();
255 on_each_cpu(__profile_flip_buffers, NULL, 1);
256 for_each_online_cpu(cpu) {
257 struct profile_hit *hits = per_cpu(cpu_profile_hits, cpu)[j];
258 for (i = 0; i < NR_PROFILE_HIT; ++i) {
259 if (!hits[i].hits) {
260 if (hits[i].pc)
261 hits[i].pc = 0;
262 continue;
263 }
264 atomic_add(hits[i].hits, &prof_buffer[hits[i].pc]);
265 hits[i].hits = hits[i].pc = 0;
266 }
267 }
268 mutex_unlock(&profile_flip_mutex);
269}
270
271static void profile_discard_flip_buffers(void)
272{
273 int i, cpu;
274
275 mutex_lock(&profile_flip_mutex);
276 i = per_cpu(cpu_profile_flip, get_cpu());
277 put_cpu();
278 on_each_cpu(__profile_flip_buffers, NULL, 1);
279 for_each_online_cpu(cpu) {
280 struct profile_hit *hits = per_cpu(cpu_profile_hits, cpu)[i];
281 memset(hits, 0, NR_PROFILE_HIT*sizeof(struct profile_hit));
282 }
283 mutex_unlock(&profile_flip_mutex);
284}
285
286static void do_profile_hits(int type, void *__pc, unsigned int nr_hits)
287{
288 unsigned long primary, secondary, flags, pc = (unsigned long)__pc;
289 int i, j, cpu;
290 struct profile_hit *hits;
291
292 pc = min((pc - (unsigned long)_stext) >> prof_shift, prof_len - 1);
293 i = primary = (pc & (NR_PROFILE_GRP - 1)) << PROFILE_GRPSHIFT;
294 secondary = (~(pc << 1) & (NR_PROFILE_GRP - 1)) << PROFILE_GRPSHIFT;
295 cpu = get_cpu();
296 hits = per_cpu(cpu_profile_hits, cpu)[per_cpu(cpu_profile_flip, cpu)];
297 if (!hits) {
298 put_cpu();
299 return;
300 }
301
302
303
304
305
306 local_irq_save(flags);
307 do {
308 for (j = 0; j < PROFILE_GRPSZ; ++j) {
309 if (hits[i + j].pc == pc) {
310 hits[i + j].hits += nr_hits;
311 goto out;
312 } else if (!hits[i + j].hits) {
313 hits[i + j].pc = pc;
314 hits[i + j].hits = nr_hits;
315 goto out;
316 }
317 }
318 i = (i + secondary) & (NR_PROFILE_HIT - 1);
319 } while (i != primary);
320
321
322
323
324
325 atomic_add(nr_hits, &prof_buffer[pc]);
326 for (i = 0; i < NR_PROFILE_HIT; ++i) {
327 atomic_add(hits[i].hits, &prof_buffer[hits[i].pc]);
328 hits[i].pc = hits[i].hits = 0;
329 }
330out:
331 local_irq_restore(flags);
332 put_cpu();
333}
334
335static int profile_dead_cpu(unsigned int cpu)
336{
337 struct page *page;
338 int i;
339
340 if (cpumask_available(prof_cpu_mask))
341 cpumask_clear_cpu(cpu, prof_cpu_mask);
342
343 for (i = 0; i < 2; i++) {
344 if (per_cpu(cpu_profile_hits, cpu)[i]) {
345 page = virt_to_page(per_cpu(cpu_profile_hits, cpu)[i]);
346 per_cpu(cpu_profile_hits, cpu)[i] = NULL;
347 __free_page(page);
348 }
349 }
350 return 0;
351}
352
353static int profile_prepare_cpu(unsigned int cpu)
354{
355 int i, node = cpu_to_mem(cpu);
356 struct page *page;
357
358 per_cpu(cpu_profile_flip, cpu) = 0;
359
360 for (i = 0; i < 2; i++) {
361 if (per_cpu(cpu_profile_hits, cpu)[i])
362 continue;
363
364 page = __alloc_pages_node(node, GFP_KERNEL | __GFP_ZERO, 0);
365 if (!page) {
366 profile_dead_cpu(cpu);
367 return -ENOMEM;
368 }
369 per_cpu(cpu_profile_hits, cpu)[i] = page_address(page);
370
371 }
372 return 0;
373}
374
375static int profile_online_cpu(unsigned int cpu)
376{
377 if (cpumask_available(prof_cpu_mask))
378 cpumask_set_cpu(cpu, prof_cpu_mask);
379
380 return 0;
381}
382
383#else
384#define profile_flip_buffers() do { } while (0)
385#define profile_discard_flip_buffers() do { } while (0)
386
387static void do_profile_hits(int type, void *__pc, unsigned int nr_hits)
388{
389 unsigned long pc;
390 pc = ((unsigned long)__pc - (unsigned long)_stext) >> prof_shift;
391 atomic_add(nr_hits, &prof_buffer[min(pc, prof_len - 1)]);
392}
393#endif
394
395void profile_hits(int type, void *__pc, unsigned int nr_hits)
396{
397 if (prof_on != type || !prof_buffer)
398 return;
399 do_profile_hits(type, __pc, nr_hits);
400}
401EXPORT_SYMBOL_GPL(profile_hits);
402
403void profile_tick(int type)
404{
405 struct pt_regs *regs = get_irq_regs();
406
407 if (!user_mode(regs) && cpumask_available(prof_cpu_mask) &&
408 cpumask_test_cpu(smp_processor_id(), prof_cpu_mask))
409 profile_hit(type, (void *)profile_pc(regs));
410}
411
412#ifdef CONFIG_PROC_FS
413#include <linux/proc_fs.h>
414#include <linux/seq_file.h>
415#include <linux/uaccess.h>
416
417static int prof_cpu_mask_proc_show(struct seq_file *m, void *v)
418{
419 seq_printf(m, "%*pb\n", cpumask_pr_args(prof_cpu_mask));
420 return 0;
421}
422
423static int prof_cpu_mask_proc_open(struct inode *inode, struct file *file)
424{
425 return single_open(file, prof_cpu_mask_proc_show, NULL);
426}
427
428static ssize_t prof_cpu_mask_proc_write(struct file *file,
429 const char __user *buffer, size_t count, loff_t *pos)
430{
431 cpumask_var_t new_value;
432 int err;
433
434 if (!zalloc_cpumask_var(&new_value, GFP_KERNEL))
435 return -ENOMEM;
436
437 err = cpumask_parse_user(buffer, count, new_value);
438 if (!err) {
439 cpumask_copy(prof_cpu_mask, new_value);
440 err = count;
441 }
442 free_cpumask_var(new_value);
443 return err;
444}
445
446static const struct proc_ops prof_cpu_mask_proc_ops = {
447 .proc_open = prof_cpu_mask_proc_open,
448 .proc_read = seq_read,
449 .proc_lseek = seq_lseek,
450 .proc_release = single_release,
451 .proc_write = prof_cpu_mask_proc_write,
452};
453
454void create_prof_cpu_mask(void)
455{
456
457 proc_create("irq/prof_cpu_mask", 0600, NULL, &prof_cpu_mask_proc_ops);
458}
459
460
461
462
463
464
465
466static ssize_t
467read_profile(struct file *file, char __user *buf, size_t count, loff_t *ppos)
468{
469 unsigned long p = *ppos;
470 ssize_t read;
471 char *pnt;
472 unsigned long sample_step = 1UL << prof_shift;
473
474 profile_flip_buffers();
475 if (p >= (prof_len+1)*sizeof(unsigned int))
476 return 0;
477 if (count > (prof_len+1)*sizeof(unsigned int) - p)
478 count = (prof_len+1)*sizeof(unsigned int) - p;
479 read = 0;
480
481 while (p < sizeof(unsigned int) && count > 0) {
482 if (put_user(*((char *)(&sample_step)+p), buf))
483 return -EFAULT;
484 buf++; p++; count--; read++;
485 }
486 pnt = (char *)prof_buffer + p - sizeof(atomic_t);
487 if (copy_to_user(buf, (void *)pnt, count))
488 return -EFAULT;
489 read += count;
490 *ppos += read;
491 return read;
492}
493
494
495
496
497
498
499
500static ssize_t write_profile(struct file *file, const char __user *buf,
501 size_t count, loff_t *ppos)
502{
503#ifdef CONFIG_SMP
504 extern int setup_profiling_timer(unsigned int multiplier);
505
506 if (count == sizeof(int)) {
507 unsigned int multiplier;
508
509 if (copy_from_user(&multiplier, buf, sizeof(int)))
510 return -EFAULT;
511
512 if (setup_profiling_timer(multiplier))
513 return -EINVAL;
514 }
515#endif
516 profile_discard_flip_buffers();
517 memset(prof_buffer, 0, prof_len * sizeof(atomic_t));
518 return count;
519}
520
521static const struct proc_ops profile_proc_ops = {
522 .proc_read = read_profile,
523 .proc_write = write_profile,
524 .proc_lseek = default_llseek,
525};
526
527int __ref create_proc_profile(void)
528{
529 struct proc_dir_entry *entry;
530#ifdef CONFIG_SMP
531 enum cpuhp_state online_state;
532#endif
533
534 int err = 0;
535
536 if (!prof_on)
537 return 0;
538#ifdef CONFIG_SMP
539 err = cpuhp_setup_state(CPUHP_PROFILE_PREPARE, "PROFILE_PREPARE",
540 profile_prepare_cpu, profile_dead_cpu);
541 if (err)
542 return err;
543
544 err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "AP_PROFILE_ONLINE",
545 profile_online_cpu, NULL);
546 if (err < 0)
547 goto err_state_prep;
548 online_state = err;
549 err = 0;
550#endif
551 entry = proc_create("profile", S_IWUSR | S_IRUGO,
552 NULL, &profile_proc_ops);
553 if (!entry)
554 goto err_state_onl;
555 proc_set_size(entry, (1 + prof_len) * sizeof(atomic_t));
556
557 return err;
558err_state_onl:
559#ifdef CONFIG_SMP
560 cpuhp_remove_state(online_state);
561err_state_prep:
562 cpuhp_remove_state(CPUHP_PROFILE_PREPARE);
563#endif
564 return err;
565}
566subsys_initcall(create_proc_profile);
567#endif
568