diff options
-rw-r--r-- | arch/arm/kernel/smp.c | 70 | ||||
-rw-r--r-- | arch/arm64/kernel/smp.c | 65 | ||||
-rw-r--r-- | include/linux/ftrace_event.h | 34 | ||||
-rw-r--r-- | include/linux/tracepoint.h | 44 | ||||
-rw-r--r-- | include/trace/events/ipi.h | 89 |
5 files changed, 214 insertions, 88 deletions
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index 7c4fada440f0..9388a3d479e1 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c | |||
@@ -47,6 +47,9 @@ | |||
47 | #include <asm/mach/arch.h> | 47 | #include <asm/mach/arch.h> |
48 | #include <asm/mpu.h> | 48 | #include <asm/mpu.h> |
49 | 49 | ||
50 | #define CREATE_TRACE_POINTS | ||
51 | #include <trace/events/ipi.h> | ||
52 | |||
50 | /* | 53 | /* |
51 | * as from 2.5, kernels no longer have an init_tasks structure | 54 | * as from 2.5, kernels no longer have an init_tasks structure |
52 | * so we need some other way of telling a new secondary core | 55 | * so we need some other way of telling a new secondary core |
@@ -430,38 +433,15 @@ void __init smp_prepare_cpus(unsigned int max_cpus) | |||
430 | } | 433 | } |
431 | } | 434 | } |
432 | 435 | ||
433 | static void (*smp_cross_call)(const struct cpumask *, unsigned int); | 436 | static void (*__smp_cross_call)(const struct cpumask *, unsigned int); |
434 | 437 | ||
435 | void __init set_smp_cross_call(void (*fn)(const struct cpumask *, unsigned int)) | 438 | void __init set_smp_cross_call(void (*fn)(const struct cpumask *, unsigned int)) |
436 | { | 439 | { |
437 | if (!smp_cross_call) | 440 | if (!__smp_cross_call) |
438 | smp_cross_call = fn; | 441 | __smp_cross_call = fn; |
439 | } | ||
440 | |||
441 | void arch_send_call_function_ipi_mask(const struct cpumask *mask) | ||
442 | { | ||
443 | smp_cross_call(mask, IPI_CALL_FUNC); | ||
444 | } | ||
445 | |||
446 | void arch_send_wakeup_ipi_mask(const struct cpumask *mask) | ||
447 | { | ||
448 | smp_cross_call(mask, IPI_WAKEUP); | ||
449 | } | ||
450 | |||
451 | void arch_send_call_function_single_ipi(int cpu) | ||
452 | { | ||
453 | smp_cross_call(cpumask_of(cpu), IPI_CALL_FUNC_SINGLE); | ||
454 | } | 442 | } |
455 | 443 | ||
456 | #ifdef CONFIG_IRQ_WORK | 444 | static const char *ipi_types[NR_IPI] __tracepoint_string = { |
457 | void arch_irq_work_raise(void) | ||
458 | { | ||
459 | if (is_smp()) | ||
460 | smp_cross_call(cpumask_of(smp_processor_id()), IPI_IRQ_WORK); | ||
461 | } | ||
462 | #endif | ||
463 | |||
464 | static const char *ipi_types[NR_IPI] = { | ||
465 | #define S(x,s) [x] = s | 445 | #define S(x,s) [x] = s |
466 | S(IPI_WAKEUP, "CPU wakeup interrupts"), | 446 | S(IPI_WAKEUP, "CPU wakeup interrupts"), |
467 | S(IPI_TIMER, "Timer broadcast interrupts"), | 447 | S(IPI_TIMER, "Timer broadcast interrupts"), |
@@ -473,6 +453,12 @@ static const char *ipi_types[NR_IPI] = { | |||
473 | S(IPI_COMPLETION, "completion interrupts"), | 453 | S(IPI_COMPLETION, "completion interrupts"), |
474 | }; | 454 | }; |
475 | 455 | ||
456 | static void smp_cross_call(const struct cpumask *target, unsigned int ipinr) | ||
457 | { | ||
458 | trace_ipi_raise(target, ipi_types[ipinr]); | ||
459 | __smp_cross_call(target, ipinr); | ||
460 | } | ||
461 | |||
476 | void show_ipi_list(struct seq_file *p, int prec) | 462 | void show_ipi_list(struct seq_file *p, int prec) |
477 | { | 463 | { |
478 | unsigned int cpu, i; | 464 | unsigned int cpu, i; |
@@ -499,6 +485,29 @@ u64 smp_irq_stat_cpu(unsigned int cpu) | |||
499 | return sum; | 485 | return sum; |
500 | } | 486 | } |
501 | 487 | ||
488 | void arch_send_call_function_ipi_mask(const struct cpumask *mask) | ||
489 | { | ||
490 | smp_cross_call(mask, IPI_CALL_FUNC); | ||
491 | } | ||
492 | |||
493 | void arch_send_wakeup_ipi_mask(const struct cpumask *mask) | ||
494 | { | ||
495 | smp_cross_call(mask, IPI_WAKEUP); | ||
496 | } | ||
497 | |||
498 | void arch_send_call_function_single_ipi(int cpu) | ||
499 | { | ||
500 | smp_cross_call(cpumask_of(cpu), IPI_CALL_FUNC_SINGLE); | ||
501 | } | ||
502 | |||
503 | #ifdef CONFIG_IRQ_WORK | ||
504 | void arch_irq_work_raise(void) | ||
505 | { | ||
506 | if (is_smp()) | ||
507 | smp_cross_call(cpumask_of(smp_processor_id()), IPI_IRQ_WORK); | ||
508 | } | ||
509 | #endif | ||
510 | |||
502 | #ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST | 511 | #ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST |
503 | void tick_broadcast(const struct cpumask *mask) | 512 | void tick_broadcast(const struct cpumask *mask) |
504 | { | 513 | { |
@@ -556,8 +565,10 @@ void handle_IPI(int ipinr, struct pt_regs *regs) | |||
556 | unsigned int cpu = smp_processor_id(); | 565 | unsigned int cpu = smp_processor_id(); |
557 | struct pt_regs *old_regs = set_irq_regs(regs); | 566 | struct pt_regs *old_regs = set_irq_regs(regs); |
558 | 567 | ||
559 | if (ipinr < NR_IPI) | 568 | if ((unsigned)ipinr < NR_IPI) { |
569 | trace_ipi_entry(ipi_types[ipinr]); | ||
560 | __inc_irq_stat(cpu, ipi_irqs[ipinr]); | 570 | __inc_irq_stat(cpu, ipi_irqs[ipinr]); |
571 | } | ||
561 | 572 | ||
562 | switch (ipinr) { | 573 | switch (ipinr) { |
563 | case IPI_WAKEUP: | 574 | case IPI_WAKEUP: |
@@ -612,6 +623,9 @@ void handle_IPI(int ipinr, struct pt_regs *regs) | |||
612 | cpu, ipinr); | 623 | cpu, ipinr); |
613 | break; | 624 | break; |
614 | } | 625 | } |
626 | |||
627 | if ((unsigned)ipinr < NR_IPI) | ||
628 | trace_ipi_exit(ipi_types[ipinr]); | ||
615 | set_irq_regs(old_regs); | 629 | set_irq_regs(old_regs); |
616 | } | 630 | } |
617 | 631 | ||
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c index 3e2f5ebbf63e..474339718105 100644 --- a/arch/arm64/kernel/smp.c +++ b/arch/arm64/kernel/smp.c | |||
@@ -51,6 +51,9 @@ | |||
51 | #include <asm/tlbflush.h> | 51 | #include <asm/tlbflush.h> |
52 | #include <asm/ptrace.h> | 52 | #include <asm/ptrace.h> |
53 | 53 | ||
54 | #define CREATE_TRACE_POINTS | ||
55 | #include <trace/events/ipi.h> | ||
56 | |||
54 | /* | 57 | /* |
55 | * as from 2.5, kernels no longer have an init_tasks structure | 58 | * as from 2.5, kernels no longer have an init_tasks structure |
56 | * so we need some other way of telling a new secondary core | 59 | * so we need some other way of telling a new secondary core |
@@ -313,8 +316,6 @@ void __init smp_prepare_boot_cpu(void) | |||
313 | set_my_cpu_offset(per_cpu_offset(smp_processor_id())); | 316 | set_my_cpu_offset(per_cpu_offset(smp_processor_id())); |
314 | } | 317 | } |
315 | 318 | ||
316 | static void (*smp_cross_call)(const struct cpumask *, unsigned int); | ||
317 | |||
318 | /* | 319 | /* |
319 | * Enumerate the possible CPU set from the device tree and build the | 320 | * Enumerate the possible CPU set from the device tree and build the |
320 | * cpu logical map array containing MPIDR values related to logical | 321 | * cpu logical map array containing MPIDR values related to logical |
@@ -469,32 +470,15 @@ void __init smp_prepare_cpus(unsigned int max_cpus) | |||
469 | } | 470 | } |
470 | } | 471 | } |
471 | 472 | ||
473 | static void (*__smp_cross_call)(const struct cpumask *, unsigned int); | ||
472 | 474 | ||
473 | void __init set_smp_cross_call(void (*fn)(const struct cpumask *, unsigned int)) | 475 | void __init set_smp_cross_call(void (*fn)(const struct cpumask *, unsigned int)) |
474 | { | 476 | { |
475 | smp_cross_call = fn; | 477 | __smp_cross_call = fn; |
476 | } | 478 | } |
477 | 479 | ||
478 | void arch_send_call_function_ipi_mask(const struct cpumask *mask) | 480 | static const char *ipi_types[NR_IPI] __tracepoint_string = { |
479 | { | 481 | #define S(x,s) [x] = s |
480 | smp_cross_call(mask, IPI_CALL_FUNC); | ||
481 | } | ||
482 | |||
483 | void arch_send_call_function_single_ipi(int cpu) | ||
484 | { | ||
485 | smp_cross_call(cpumask_of(cpu), IPI_CALL_FUNC_SINGLE); | ||
486 | } | ||
487 | |||
488 | #ifdef CONFIG_IRQ_WORK | ||
489 | void arch_irq_work_raise(void) | ||
490 | { | ||
491 | if (smp_cross_call) | ||
492 | smp_cross_call(cpumask_of(smp_processor_id()), IPI_IRQ_WORK); | ||
493 | } | ||
494 | #endif | ||
495 | |||
496 | static const char *ipi_types[NR_IPI] = { | ||
497 | #define S(x,s) [x - IPI_RESCHEDULE] = s | ||
498 | S(IPI_RESCHEDULE, "Rescheduling interrupts"), | 482 | S(IPI_RESCHEDULE, "Rescheduling interrupts"), |
499 | S(IPI_CALL_FUNC, "Function call interrupts"), | 483 | S(IPI_CALL_FUNC, "Function call interrupts"), |
500 | S(IPI_CALL_FUNC_SINGLE, "Single function call interrupts"), | 484 | S(IPI_CALL_FUNC_SINGLE, "Single function call interrupts"), |
@@ -503,12 +487,18 @@ static const char *ipi_types[NR_IPI] = { | |||
503 | S(IPI_IRQ_WORK, "IRQ work interrupts"), | 487 | S(IPI_IRQ_WORK, "IRQ work interrupts"), |
504 | }; | 488 | }; |
505 | 489 | ||
490 | static void smp_cross_call(const struct cpumask *target, unsigned int ipinr) | ||
491 | { | ||
492 | trace_ipi_raise(target, ipi_types[ipinr]); | ||
493 | __smp_cross_call(target, ipinr); | ||
494 | } | ||
495 | |||
506 | void show_ipi_list(struct seq_file *p, int prec) | 496 | void show_ipi_list(struct seq_file *p, int prec) |
507 | { | 497 | { |
508 | unsigned int cpu, i; | 498 | unsigned int cpu, i; |
509 | 499 | ||
510 | for (i = 0; i < NR_IPI; i++) { | 500 | for (i = 0; i < NR_IPI; i++) { |
511 | seq_printf(p, "%*s%u:%s", prec - 1, "IPI", i + IPI_RESCHEDULE, | 501 | seq_printf(p, "%*s%u:%s", prec - 1, "IPI", i, |
512 | prec >= 4 ? " " : ""); | 502 | prec >= 4 ? " " : ""); |
513 | for_each_online_cpu(cpu) | 503 | for_each_online_cpu(cpu) |
514 | seq_printf(p, "%10u ", | 504 | seq_printf(p, "%10u ", |
@@ -528,6 +518,24 @@ u64 smp_irq_stat_cpu(unsigned int cpu) | |||
528 | return sum; | 518 | return sum; |
529 | } | 519 | } |
530 | 520 | ||
521 | void arch_send_call_function_ipi_mask(const struct cpumask *mask) | ||
522 | { | ||
523 | smp_cross_call(mask, IPI_CALL_FUNC); | ||
524 | } | ||
525 | |||
526 | void arch_send_call_function_single_ipi(int cpu) | ||
527 | { | ||
528 | smp_cross_call(cpumask_of(cpu), IPI_CALL_FUNC_SINGLE); | ||
529 | } | ||
530 | |||
531 | #ifdef CONFIG_IRQ_WORK | ||
532 | void arch_irq_work_raise(void) | ||
533 | { | ||
534 | if (__smp_cross_call) | ||
535 | smp_cross_call(cpumask_of(smp_processor_id()), IPI_IRQ_WORK); | ||
536 | } | ||
537 | #endif | ||
538 | |||
531 | static DEFINE_RAW_SPINLOCK(stop_lock); | 539 | static DEFINE_RAW_SPINLOCK(stop_lock); |
532 | 540 | ||
533 | /* | 541 | /* |
@@ -559,8 +567,10 @@ void handle_IPI(int ipinr, struct pt_regs *regs) | |||
559 | unsigned int cpu = smp_processor_id(); | 567 | unsigned int cpu = smp_processor_id(); |
560 | struct pt_regs *old_regs = set_irq_regs(regs); | 568 | struct pt_regs *old_regs = set_irq_regs(regs); |
561 | 569 | ||
562 | if (ipinr >= IPI_RESCHEDULE && ipinr < IPI_RESCHEDULE + NR_IPI) | 570 | if ((unsigned)ipinr < NR_IPI) { |
563 | __inc_irq_stat(cpu, ipi_irqs[ipinr - IPI_RESCHEDULE]); | 571 | trace_ipi_entry(ipi_types[ipinr]); |
572 | __inc_irq_stat(cpu, ipi_irqs[ipinr]); | ||
573 | } | ||
564 | 574 | ||
565 | switch (ipinr) { | 575 | switch (ipinr) { |
566 | case IPI_RESCHEDULE: | 576 | case IPI_RESCHEDULE: |
@@ -605,6 +615,9 @@ void handle_IPI(int ipinr, struct pt_regs *regs) | |||
605 | pr_crit("CPU%u: Unknown IPI message 0x%x\n", cpu, ipinr); | 615 | pr_crit("CPU%u: Unknown IPI message 0x%x\n", cpu, ipinr); |
606 | break; | 616 | break; |
607 | } | 617 | } |
618 | |||
619 | if ((unsigned)ipinr < NR_IPI) | ||
620 | trace_ipi_exit(ipi_types[ipinr]); | ||
608 | set_irq_regs(old_regs); | 621 | set_irq_regs(old_regs); |
609 | } | 622 | } |
610 | 623 | ||
diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h index 06c6faa9e5cc..28672e87e910 100644 --- a/include/linux/ftrace_event.h +++ b/include/linux/ftrace_event.h | |||
@@ -571,40 +571,6 @@ do { \ | |||
571 | __trace_printk(ip, fmt, ##args); \ | 571 | __trace_printk(ip, fmt, ##args); \ |
572 | } while (0) | 572 | } while (0) |
573 | 573 | ||
574 | /** | ||
575 | * tracepoint_string - register constant persistent string to trace system | ||
576 | * @str - a constant persistent string that will be referenced in tracepoints | ||
577 | * | ||
578 | * If constant strings are being used in tracepoints, it is faster and | ||
579 | * more efficient to just save the pointer to the string and reference | ||
580 | * that with a printf "%s" instead of saving the string in the ring buffer | ||
581 | * and wasting space and time. | ||
582 | * | ||
583 | * The problem with the above approach is that userspace tools that read | ||
584 | * the binary output of the trace buffers do not have access to the string. | ||
585 | * Instead they just show the address of the string which is not very | ||
586 | * useful to users. | ||
587 | * | ||
588 | * With tracepoint_string(), the string will be registered to the tracing | ||
589 | * system and exported to userspace via the debugfs/tracing/printk_formats | ||
590 | * file that maps the string address to the string text. This way userspace | ||
591 | * tools that read the binary buffers have a way to map the pointers to | ||
592 | * the ASCII strings they represent. | ||
593 | * | ||
594 | * The @str used must be a constant string and persistent as it would not | ||
595 | * make sense to show a string that no longer exists. But it is still fine | ||
596 | * to be used with modules, because when modules are unloaded, if they | ||
597 | * had tracepoints, the ring buffers are cleared too. As long as the string | ||
598 | * does not change during the life of the module, it is fine to use | ||
599 | * tracepoint_string() within a module. | ||
600 | */ | ||
601 | #define tracepoint_string(str) \ | ||
602 | ({ \ | ||
603 | static const char *___tp_str __tracepoint_string = str; \ | ||
604 | ___tp_str; \ | ||
605 | }) | ||
606 | #define __tracepoint_string __attribute__((section("__tracepoint_str"))) | ||
607 | |||
608 | #ifdef CONFIG_PERF_EVENTS | 574 | #ifdef CONFIG_PERF_EVENTS |
609 | struct perf_event; | 575 | struct perf_event; |
610 | 576 | ||
diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h index 2e2a5f7717e5..b1293f15f592 100644 --- a/include/linux/tracepoint.h +++ b/include/linux/tracepoint.h | |||
@@ -249,6 +249,50 @@ extern void syscall_unregfunc(void); | |||
249 | 249 | ||
250 | #endif /* CONFIG_TRACEPOINTS */ | 250 | #endif /* CONFIG_TRACEPOINTS */ |
251 | 251 | ||
252 | #ifdef CONFIG_TRACING | ||
253 | /** | ||
254 | * tracepoint_string - register constant persistent string to trace system | ||
255 | * @str - a constant persistent string that will be referenced in tracepoints | ||
256 | * | ||
257 | * If constant strings are being used in tracepoints, it is faster and | ||
258 | * more efficient to just save the pointer to the string and reference | ||
259 | * that with a printf "%s" instead of saving the string in the ring buffer | ||
260 | * and wasting space and time. | ||
261 | * | ||
262 | * The problem with the above approach is that userspace tools that read | ||
263 | * the binary output of the trace buffers do not have access to the string. | ||
264 | * Instead they just show the address of the string which is not very | ||
265 | * useful to users. | ||
266 | * | ||
267 | * With tracepoint_string(), the string will be registered to the tracing | ||
268 | * system and exported to userspace via the debugfs/tracing/printk_formats | ||
269 | * file that maps the string address to the string text. This way userspace | ||
270 | * tools that read the binary buffers have a way to map the pointers to | ||
271 | * the ASCII strings they represent. | ||
272 | * | ||
273 | * The @str used must be a constant string and persistent as it would not | ||
274 | * make sense to show a string that no longer exists. But it is still fine | ||
275 | * to be used with modules, because when modules are unloaded, if they | ||
276 | * had tracepoints, the ring buffers are cleared too. As long as the string | ||
277 | * does not change during the life of the module, it is fine to use | ||
278 | * tracepoint_string() within a module. | ||
279 | */ | ||
280 | #define tracepoint_string(str) \ | ||
281 | ({ \ | ||
282 | static const char *___tp_str __tracepoint_string = str; \ | ||
283 | ___tp_str; \ | ||
284 | }) | ||
285 | #define __tracepoint_string __attribute__((section("__tracepoint_str"))) | ||
286 | #else | ||
287 | /* | ||
288 | * tracepoint_string() is used to save the string address for userspace | ||
289 | * tracing tools. When tracing isn't configured, there's no need to save | ||
290 | * anything. | ||
291 | */ | ||
292 | # define tracepoint_string(str) str | ||
293 | # define __tracepoint_string | ||
294 | #endif | ||
295 | |||
252 | /* | 296 | /* |
253 | * The need for the DECLARE_TRACE_NOARGS() is to handle the prototype | 297 | * The need for the DECLARE_TRACE_NOARGS() is to handle the prototype |
254 | * (void). "void" is a special value in a function prototype and can | 298 | * (void). "void" is a special value in a function prototype and can |
diff --git a/include/trace/events/ipi.h b/include/trace/events/ipi.h new file mode 100644 index 000000000000..834a7362a610 --- /dev/null +++ b/include/trace/events/ipi.h | |||
@@ -0,0 +1,89 @@ | |||
1 | #undef TRACE_SYSTEM | ||
2 | #define TRACE_SYSTEM ipi | ||
3 | |||
4 | #if !defined(_TRACE_IPI_H) || defined(TRACE_HEADER_MULTI_READ) | ||
5 | #define _TRACE_IPI_H | ||
6 | |||
7 | #include <linux/tracepoint.h> | ||
8 | |||
9 | /** | ||
10 | * ipi_raise - called when a smp cross call is made | ||
11 | * | ||
12 | * @mask: mask of recipient CPUs for the IPI | ||
13 | * @reason: string identifying the IPI purpose | ||
14 | * | ||
15 | * It is necessary for @reason to be a static string declared with | ||
16 | * __tracepoint_string. | ||
17 | */ | ||
18 | TRACE_EVENT(ipi_raise, | ||
19 | |||
20 | TP_PROTO(const struct cpumask *mask, const char *reason), | ||
21 | |||
22 | TP_ARGS(mask, reason), | ||
23 | |||
24 | TP_STRUCT__entry( | ||
25 | __bitmask(target_cpus, nr_cpumask_bits) | ||
26 | __field(const char *, reason) | ||
27 | ), | ||
28 | |||
29 | TP_fast_assign( | ||
30 | __assign_bitmask(target_cpus, cpumask_bits(mask), nr_cpumask_bits); | ||
31 | __entry->reason = reason; | ||
32 | ), | ||
33 | |||
34 | TP_printk("target_mask=%s (%s)", __get_bitmask(target_cpus), __entry->reason) | ||
35 | ); | ||
36 | |||
37 | DECLARE_EVENT_CLASS(ipi_handler, | ||
38 | |||
39 | TP_PROTO(const char *reason), | ||
40 | |||
41 | TP_ARGS(reason), | ||
42 | |||
43 | TP_STRUCT__entry( | ||
44 | __field(const char *, reason) | ||
45 | ), | ||
46 | |||
47 | TP_fast_assign( | ||
48 | __entry->reason = reason; | ||
49 | ), | ||
50 | |||
51 | TP_printk("(%s)", __entry->reason) | ||
52 | ); | ||
53 | |||
54 | /** | ||
55 | * ipi_entry - called immediately before the IPI handler | ||
56 | * | ||
57 | * @reason: string identifying the IPI purpose | ||
58 | * | ||
59 | * It is necessary for @reason to be a static string declared with | ||
60 | * __tracepoint_string, ideally the same as used with trace_ipi_raise | ||
61 | * for that IPI. | ||
62 | */ | ||
63 | DEFINE_EVENT(ipi_handler, ipi_entry, | ||
64 | |||
65 | TP_PROTO(const char *reason), | ||
66 | |||
67 | TP_ARGS(reason) | ||
68 | ); | ||
69 | |||
70 | /** | ||
71 | * ipi_exit - called immediately after the IPI handler returns | ||
72 | * | ||
73 | * @reason: string identifying the IPI purpose | ||
74 | * | ||
75 | * It is necessary for @reason to be a static string declared with | ||
76 | * __tracepoint_string, ideally the same as used with trace_ipi_raise for | ||
77 | * that IPI. | ||
78 | */ | ||
79 | DEFINE_EVENT(ipi_handler, ipi_exit, | ||
80 | |||
81 | TP_PROTO(const char *reason), | ||
82 | |||
83 | TP_ARGS(reason) | ||
84 | ); | ||
85 | |||
86 | #endif /* _TRACE_IPI_H */ | ||
87 | |||
88 | /* This part must be outside protection */ | ||
89 | #include <trace/define_trace.h> | ||