diff options
-rw-r--r-- | include/asm-generic/vmlinux.lds.h | 6 | ||||
-rw-r--r-- | include/linux/module.h | 17 | ||||
-rw-r--r-- | include/linux/tracepoint.h | 127 | ||||
-rw-r--r-- | init/Kconfig | 7 | ||||
-rw-r--r-- | kernel/Makefile | 1 | ||||
-rw-r--r-- | kernel/module.c | 66 | ||||
-rw-r--r-- | kernel/tracepoint.c | 476 |
7 files changed, 698 insertions, 2 deletions
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index 7440a0dceddb..3d8e472a09c8 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h | |||
@@ -52,7 +52,10 @@ | |||
52 | . = ALIGN(8); \ | 52 | . = ALIGN(8); \ |
53 | VMLINUX_SYMBOL(__start___markers) = .; \ | 53 | VMLINUX_SYMBOL(__start___markers) = .; \ |
54 | *(__markers) \ | 54 | *(__markers) \ |
55 | VMLINUX_SYMBOL(__stop___markers) = .; | 55 | VMLINUX_SYMBOL(__stop___markers) = .; \ |
56 | VMLINUX_SYMBOL(__start___tracepoints) = .; \ | ||
57 | *(__tracepoints) \ | ||
58 | VMLINUX_SYMBOL(__stop___tracepoints) = .; | ||
56 | 59 | ||
57 | #define RO_DATA(align) \ | 60 | #define RO_DATA(align) \ |
58 | . = ALIGN((align)); \ | 61 | . = ALIGN((align)); \ |
@@ -61,6 +64,7 @@ | |||
61 | *(.rodata) *(.rodata.*) \ | 64 | *(.rodata) *(.rodata.*) \ |
62 | *(__vermagic) /* Kernel version magic */ \ | 65 | *(__vermagic) /* Kernel version magic */ \ |
63 | *(__markers_strings) /* Markers: strings */ \ | 66 | *(__markers_strings) /* Markers: strings */ \ |
67 | *(__tracepoints_strings)/* Tracepoints: strings */ \ | ||
64 | } \ | 68 | } \ |
65 | \ | 69 | \ |
66 | .rodata1 : AT(ADDR(.rodata1) - LOAD_OFFSET) { \ | 70 | .rodata1 : AT(ADDR(.rodata1) - LOAD_OFFSET) { \ |
diff --git a/include/linux/module.h b/include/linux/module.h index 68e09557c951..8b6113503863 100644 --- a/include/linux/module.h +++ b/include/linux/module.h | |||
@@ -16,6 +16,7 @@ | |||
16 | #include <linux/kobject.h> | 16 | #include <linux/kobject.h> |
17 | #include <linux/moduleparam.h> | 17 | #include <linux/moduleparam.h> |
18 | #include <linux/marker.h> | 18 | #include <linux/marker.h> |
19 | #include <linux/tracepoint.h> | ||
19 | #include <asm/local.h> | 20 | #include <asm/local.h> |
20 | 21 | ||
21 | #include <asm/module.h> | 22 | #include <asm/module.h> |
@@ -331,6 +332,10 @@ struct module | |||
331 | struct marker *markers; | 332 | struct marker *markers; |
332 | unsigned int num_markers; | 333 | unsigned int num_markers; |
333 | #endif | 334 | #endif |
335 | #ifdef CONFIG_TRACEPOINTS | ||
336 | struct tracepoint *tracepoints; | ||
337 | unsigned int num_tracepoints; | ||
338 | #endif | ||
334 | 339 | ||
335 | #ifdef CONFIG_MODULE_UNLOAD | 340 | #ifdef CONFIG_MODULE_UNLOAD |
336 | /* What modules depend on me? */ | 341 | /* What modules depend on me? */ |
@@ -454,6 +459,9 @@ extern void print_modules(void); | |||
454 | 459 | ||
455 | extern void module_update_markers(void); | 460 | extern void module_update_markers(void); |
456 | 461 | ||
462 | extern void module_update_tracepoints(void); | ||
463 | extern int module_get_iter_tracepoints(struct tracepoint_iter *iter); | ||
464 | |||
457 | #else /* !CONFIG_MODULES... */ | 465 | #else /* !CONFIG_MODULES... */ |
458 | #define EXPORT_SYMBOL(sym) | 466 | #define EXPORT_SYMBOL(sym) |
459 | #define EXPORT_SYMBOL_GPL(sym) | 467 | #define EXPORT_SYMBOL_GPL(sym) |
@@ -558,6 +566,15 @@ static inline void module_update_markers(void) | |||
558 | { | 566 | { |
559 | } | 567 | } |
560 | 568 | ||
569 | static inline void module_update_tracepoints(void) | ||
570 | { | ||
571 | } | ||
572 | |||
573 | static inline int module_get_iter_tracepoints(struct tracepoint_iter *iter) | ||
574 | { | ||
575 | return 0; | ||
576 | } | ||
577 | |||
561 | #endif /* CONFIG_MODULES */ | 578 | #endif /* CONFIG_MODULES */ |
562 | 579 | ||
563 | struct device_driver; | 580 | struct device_driver; |
diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h new file mode 100644 index 000000000000..e623a6fca5c3 --- /dev/null +++ b/include/linux/tracepoint.h | |||
@@ -0,0 +1,127 @@ | |||
1 | #ifndef _LINUX_TRACEPOINT_H | ||
2 | #define _LINUX_TRACEPOINT_H | ||
3 | |||
4 | /* | ||
5 | * Kernel Tracepoint API. | ||
6 | * | ||
7 | * See Documentation/tracepoint.txt. | ||
8 | * | ||
9 | * (C) Copyright 2008 Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca> | ||
10 | * | ||
11 | * Heavily inspired from the Linux Kernel Markers. | ||
12 | * | ||
13 | * This file is released under the GPLv2. | ||
14 | * See the file COPYING for more details. | ||
15 | */ | ||
16 | |||
17 | #include <linux/types.h> | ||
18 | #include <linux/rcupdate.h> | ||
19 | |||
20 | struct module; | ||
21 | struct tracepoint; | ||
22 | |||
23 | struct tracepoint { | ||
24 | const char *name; /* Tracepoint name */ | ||
25 | int state; /* State. */ | ||
26 | void **funcs; | ||
27 | } __attribute__((aligned(8))); | ||
28 | |||
29 | |||
30 | #define TPPROTO(args...) args | ||
31 | #define TPARGS(args...) args | ||
32 | |||
33 | #ifdef CONFIG_TRACEPOINTS | ||
34 | |||
35 | /* | ||
36 | * it_func[0] is never NULL because there is at least one element in the array | ||
37 | * when the array itself is non NULL. | ||
38 | */ | ||
39 | #define __DO_TRACE(tp, proto, args) \ | ||
40 | do { \ | ||
41 | void **it_func; \ | ||
42 | \ | ||
43 | rcu_read_lock_sched(); \ | ||
44 | it_func = rcu_dereference((tp)->funcs); \ | ||
45 | if (it_func) { \ | ||
46 | do { \ | ||
47 | ((void(*)(proto))(*it_func))(args); \ | ||
48 | } while (*(++it_func)); \ | ||
49 | } \ | ||
50 | rcu_read_unlock_sched(); \ | ||
51 | } while (0) | ||
52 | |||
53 | /* | ||
54 | * Make sure the alignment of the structure in the __tracepoints section will | ||
55 | * not add unwanted padding between the beginning of the section and the | ||
56 | * structure. Force alignment to the same alignment as the section start. | ||
57 | */ | ||
58 | #define DEFINE_TRACE(name, proto, args) \ | ||
59 | static inline void trace_##name(proto) \ | ||
60 | { \ | ||
61 | static const char __tpstrtab_##name[] \ | ||
62 | __attribute__((section("__tracepoints_strings"))) \ | ||
63 | = #name ":" #proto; \ | ||
64 | static struct tracepoint __tracepoint_##name \ | ||
65 | __attribute__((section("__tracepoints"), aligned(8))) = \ | ||
66 | { __tpstrtab_##name, 0, NULL }; \ | ||
67 | if (unlikely(__tracepoint_##name.state)) \ | ||
68 | __DO_TRACE(&__tracepoint_##name, \ | ||
69 | TPPROTO(proto), TPARGS(args)); \ | ||
70 | } \ | ||
71 | static inline int register_trace_##name(void (*probe)(proto)) \ | ||
72 | { \ | ||
73 | return tracepoint_probe_register(#name ":" #proto, \ | ||
74 | (void *)probe); \ | ||
75 | } \ | ||
76 | static inline void unregister_trace_##name(void (*probe)(proto))\ | ||
77 | { \ | ||
78 | tracepoint_probe_unregister(#name ":" #proto, \ | ||
79 | (void *)probe); \ | ||
80 | } | ||
81 | |||
82 | extern void tracepoint_update_probe_range(struct tracepoint *begin, | ||
83 | struct tracepoint *end); | ||
84 | |||
85 | #else /* !CONFIG_TRACEPOINTS */ | ||
86 | #define DEFINE_TRACE(name, proto, args) \ | ||
87 | static inline void _do_trace_##name(struct tracepoint *tp, proto) \ | ||
88 | { } \ | ||
89 | static inline void trace_##name(proto) \ | ||
90 | { } \ | ||
91 | static inline int register_trace_##name(void (*probe)(proto)) \ | ||
92 | { \ | ||
93 | return -ENOSYS; \ | ||
94 | } \ | ||
95 | static inline void unregister_trace_##name(void (*probe)(proto))\ | ||
96 | { } | ||
97 | |||
98 | static inline void tracepoint_update_probe_range(struct tracepoint *begin, | ||
99 | struct tracepoint *end) | ||
100 | { } | ||
101 | #endif /* CONFIG_TRACEPOINTS */ | ||
102 | |||
103 | /* | ||
104 | * Connect a probe to a tracepoint. | ||
105 | * Internal API, should not be used directly. | ||
106 | */ | ||
107 | extern int tracepoint_probe_register(const char *name, void *probe); | ||
108 | |||
109 | /* | ||
110 | * Disconnect a probe from a tracepoint. | ||
111 | * Internal API, should not be used directly. | ||
112 | */ | ||
113 | extern int tracepoint_probe_unregister(const char *name, void *probe); | ||
114 | |||
115 | struct tracepoint_iter { | ||
116 | struct module *module; | ||
117 | struct tracepoint *tracepoint; | ||
118 | }; | ||
119 | |||
120 | extern void tracepoint_iter_start(struct tracepoint_iter *iter); | ||
121 | extern void tracepoint_iter_next(struct tracepoint_iter *iter); | ||
122 | extern void tracepoint_iter_stop(struct tracepoint_iter *iter); | ||
123 | extern void tracepoint_iter_reset(struct tracepoint_iter *iter); | ||
124 | extern int tracepoint_get_iter_range(struct tracepoint **tracepoint, | ||
125 | struct tracepoint *begin, struct tracepoint *end); | ||
126 | |||
127 | #endif | ||
diff --git a/init/Kconfig b/init/Kconfig index c11da38837e5..70082678a914 100644 --- a/init/Kconfig +++ b/init/Kconfig | |||
@@ -771,6 +771,13 @@ config PROFILING | |||
771 | Say Y here to enable the extended profiling support mechanisms used | 771 | Say Y here to enable the extended profiling support mechanisms used |
772 | by profilers such as OProfile. | 772 | by profilers such as OProfile. |
773 | 773 | ||
774 | config TRACEPOINTS | ||
775 | bool "Activate tracepoints" | ||
776 | default y | ||
777 | help | ||
778 | Place an empty function call at each tracepoint site. Can be | ||
779 | dynamically changed for a probe function. | ||
780 | |||
774 | config MARKERS | 781 | config MARKERS |
775 | bool "Activate markers" | 782 | bool "Activate markers" |
776 | help | 783 | help |
diff --git a/kernel/Makefile b/kernel/Makefile index 4e1d7df7c3e2..8f9ce7ec21b6 100644 --- a/kernel/Makefile +++ b/kernel/Makefile | |||
@@ -83,6 +83,7 @@ obj-$(CONFIG_SYSCTL) += utsname_sysctl.o | |||
83 | obj-$(CONFIG_TASK_DELAY_ACCT) += delayacct.o | 83 | obj-$(CONFIG_TASK_DELAY_ACCT) += delayacct.o |
84 | obj-$(CONFIG_TASKSTATS) += taskstats.o tsacct.o | 84 | obj-$(CONFIG_TASKSTATS) += taskstats.o tsacct.o |
85 | obj-$(CONFIG_MARKERS) += marker.o | 85 | obj-$(CONFIG_MARKERS) += marker.o |
86 | obj-$(CONFIG_TRACEPOINTS) += tracepoint.o | ||
86 | obj-$(CONFIG_LATENCYTOP) += latencytop.o | 87 | obj-$(CONFIG_LATENCYTOP) += latencytop.o |
87 | obj-$(CONFIG_HAVE_GENERIC_DMA_COHERENT) += dma-coherent.o | 88 | obj-$(CONFIG_HAVE_GENERIC_DMA_COHERENT) += dma-coherent.o |
88 | obj-$(CONFIG_FTRACE) += trace/ | 89 | obj-$(CONFIG_FTRACE) += trace/ |
diff --git a/kernel/module.c b/kernel/module.c index 9db11911e04b..661d73db786e 100644 --- a/kernel/module.c +++ b/kernel/module.c | |||
@@ -46,6 +46,7 @@ | |||
46 | #include <asm/cacheflush.h> | 46 | #include <asm/cacheflush.h> |
47 | #include <linux/license.h> | 47 | #include <linux/license.h> |
48 | #include <asm/sections.h> | 48 | #include <asm/sections.h> |
49 | #include <linux/tracepoint.h> | ||
49 | 50 | ||
50 | #if 0 | 51 | #if 0 |
51 | #define DEBUGP printk | 52 | #define DEBUGP printk |
@@ -1831,6 +1832,8 @@ static noinline struct module *load_module(void __user *umod, | |||
1831 | #endif | 1832 | #endif |
1832 | unsigned int markersindex; | 1833 | unsigned int markersindex; |
1833 | unsigned int markersstringsindex; | 1834 | unsigned int markersstringsindex; |
1835 | unsigned int tracepointsindex; | ||
1836 | unsigned int tracepointsstringsindex; | ||
1834 | struct module *mod; | 1837 | struct module *mod; |
1835 | long err = 0; | 1838 | long err = 0; |
1836 | void *percpu = NULL, *ptr = NULL; /* Stops spurious gcc warning */ | 1839 | void *percpu = NULL, *ptr = NULL; /* Stops spurious gcc warning */ |
@@ -2117,6 +2120,9 @@ static noinline struct module *load_module(void __user *umod, | |||
2117 | markersindex = find_sec(hdr, sechdrs, secstrings, "__markers"); | 2120 | markersindex = find_sec(hdr, sechdrs, secstrings, "__markers"); |
2118 | markersstringsindex = find_sec(hdr, sechdrs, secstrings, | 2121 | markersstringsindex = find_sec(hdr, sechdrs, secstrings, |
2119 | "__markers_strings"); | 2122 | "__markers_strings"); |
2123 | tracepointsindex = find_sec(hdr, sechdrs, secstrings, "__tracepoints"); | ||
2124 | tracepointsstringsindex = find_sec(hdr, sechdrs, secstrings, | ||
2125 | "__tracepoints_strings"); | ||
2120 | 2126 | ||
2121 | /* Now do relocations. */ | 2127 | /* Now do relocations. */ |
2122 | for (i = 1; i < hdr->e_shnum; i++) { | 2128 | for (i = 1; i < hdr->e_shnum; i++) { |
@@ -2144,6 +2150,12 @@ static noinline struct module *load_module(void __user *umod, | |||
2144 | mod->num_markers = | 2150 | mod->num_markers = |
2145 | sechdrs[markersindex].sh_size / sizeof(*mod->markers); | 2151 | sechdrs[markersindex].sh_size / sizeof(*mod->markers); |
2146 | #endif | 2152 | #endif |
2153 | #ifdef CONFIG_TRACEPOINTS | ||
2154 | mod->tracepoints = (void *)sechdrs[tracepointsindex].sh_addr; | ||
2155 | mod->num_tracepoints = | ||
2156 | sechdrs[tracepointsindex].sh_size / sizeof(*mod->tracepoints); | ||
2157 | #endif | ||
2158 | |||
2147 | 2159 | ||
2148 | /* Find duplicate symbols */ | 2160 | /* Find duplicate symbols */ |
2149 | err = verify_export_symbols(mod); | 2161 | err = verify_export_symbols(mod); |
@@ -2162,11 +2174,16 @@ static noinline struct module *load_module(void __user *umod, | |||
2162 | 2174 | ||
2163 | add_kallsyms(mod, sechdrs, symindex, strindex, secstrings); | 2175 | add_kallsyms(mod, sechdrs, symindex, strindex, secstrings); |
2164 | 2176 | ||
2177 | if (!mod->taints) { | ||
2165 | #ifdef CONFIG_MARKERS | 2178 | #ifdef CONFIG_MARKERS |
2166 | if (!mod->taints) | ||
2167 | marker_update_probe_range(mod->markers, | 2179 | marker_update_probe_range(mod->markers, |
2168 | mod->markers + mod->num_markers); | 2180 | mod->markers + mod->num_markers); |
2169 | #endif | 2181 | #endif |
2182 | #ifdef CONFIG_TRACEPOINTS | ||
2183 | tracepoint_update_probe_range(mod->tracepoints, | ||
2184 | mod->tracepoints + mod->num_tracepoints); | ||
2185 | #endif | ||
2186 | } | ||
2170 | err = module_finalize(hdr, sechdrs, mod); | 2187 | err = module_finalize(hdr, sechdrs, mod); |
2171 | if (err < 0) | 2188 | if (err < 0) |
2172 | goto cleanup; | 2189 | goto cleanup; |
@@ -2717,3 +2734,50 @@ void module_update_markers(void) | |||
2717 | mutex_unlock(&module_mutex); | 2734 | mutex_unlock(&module_mutex); |
2718 | } | 2735 | } |
2719 | #endif | 2736 | #endif |
2737 | |||
2738 | #ifdef CONFIG_TRACEPOINTS | ||
2739 | void module_update_tracepoints(void) | ||
2740 | { | ||
2741 | struct module *mod; | ||
2742 | |||
2743 | mutex_lock(&module_mutex); | ||
2744 | list_for_each_entry(mod, &modules, list) | ||
2745 | if (!mod->taints) | ||
2746 | tracepoint_update_probe_range(mod->tracepoints, | ||
2747 | mod->tracepoints + mod->num_tracepoints); | ||
2748 | mutex_unlock(&module_mutex); | ||
2749 | } | ||
2750 | |||
2751 | /* | ||
2752 | * Returns 0 if current not found. | ||
2753 | * Returns 1 if current found. | ||
2754 | */ | ||
2755 | int module_get_iter_tracepoints(struct tracepoint_iter *iter) | ||
2756 | { | ||
2757 | struct module *iter_mod; | ||
2758 | int found = 0; | ||
2759 | |||
2760 | mutex_lock(&module_mutex); | ||
2761 | list_for_each_entry(iter_mod, &modules, list) { | ||
2762 | if (!iter_mod->taints) { | ||
2763 | /* | ||
2764 | * Sorted module list | ||
2765 | */ | ||
2766 | if (iter_mod < iter->module) | ||
2767 | continue; | ||
2768 | else if (iter_mod > iter->module) | ||
2769 | iter->tracepoint = NULL; | ||
2770 | found = tracepoint_get_iter_range(&iter->tracepoint, | ||
2771 | iter_mod->tracepoints, | ||
2772 | iter_mod->tracepoints | ||
2773 | + iter_mod->num_tracepoints); | ||
2774 | if (found) { | ||
2775 | iter->module = iter_mod; | ||
2776 | break; | ||
2777 | } | ||
2778 | } | ||
2779 | } | ||
2780 | mutex_unlock(&module_mutex); | ||
2781 | return found; | ||
2782 | } | ||
2783 | #endif | ||
diff --git a/kernel/tracepoint.c b/kernel/tracepoint.c new file mode 100644 index 000000000000..42e86ddbd2a0 --- /dev/null +++ b/kernel/tracepoint.c | |||
@@ -0,0 +1,476 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2008 Mathieu Desnoyers | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation; either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program; if not, write to the Free Software | ||
16 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | */ | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/mutex.h> | ||
20 | #include <linux/types.h> | ||
21 | #include <linux/jhash.h> | ||
22 | #include <linux/list.h> | ||
23 | #include <linux/rcupdate.h> | ||
24 | #include <linux/tracepoint.h> | ||
25 | #include <linux/err.h> | ||
26 | #include <linux/slab.h> | ||
27 | |||
28 | extern struct tracepoint __start___tracepoints[]; | ||
29 | extern struct tracepoint __stop___tracepoints[]; | ||
30 | |||
31 | /* Set to 1 to enable tracepoint debug output */ | ||
32 | static const int tracepoint_debug; | ||
33 | |||
34 | /* | ||
35 | * tracepoints_mutex nests inside module_mutex. Tracepoints mutex protects the | ||
36 | * builtin and module tracepoints and the hash table. | ||
37 | */ | ||
38 | static DEFINE_MUTEX(tracepoints_mutex); | ||
39 | |||
40 | /* | ||
41 | * Tracepoint hash table, containing the active tracepoints. | ||
42 | * Protected by tracepoints_mutex. | ||
43 | */ | ||
44 | #define TRACEPOINT_HASH_BITS 6 | ||
45 | #define TRACEPOINT_TABLE_SIZE (1 << TRACEPOINT_HASH_BITS) | ||
46 | |||
47 | /* | ||
48 | * Note about RCU : | ||
49 | * It is used to to delay the free of multiple probes array until a quiescent | ||
50 | * state is reached. | ||
51 | * Tracepoint entries modifications are protected by the tracepoints_mutex. | ||
52 | */ | ||
53 | struct tracepoint_entry { | ||
54 | struct hlist_node hlist; | ||
55 | void **funcs; | ||
56 | int refcount; /* Number of times armed. 0 if disarmed. */ | ||
57 | struct rcu_head rcu; | ||
58 | void *oldptr; | ||
59 | unsigned char rcu_pending:1; | ||
60 | char name[0]; | ||
61 | }; | ||
62 | |||
63 | static struct hlist_head tracepoint_table[TRACEPOINT_TABLE_SIZE]; | ||
64 | |||
65 | static void free_old_closure(struct rcu_head *head) | ||
66 | { | ||
67 | struct tracepoint_entry *entry = container_of(head, | ||
68 | struct tracepoint_entry, rcu); | ||
69 | kfree(entry->oldptr); | ||
70 | /* Make sure we free the data before setting the pending flag to 0 */ | ||
71 | smp_wmb(); | ||
72 | entry->rcu_pending = 0; | ||
73 | } | ||
74 | |||
75 | static void tracepoint_entry_free_old(struct tracepoint_entry *entry, void *old) | ||
76 | { | ||
77 | if (!old) | ||
78 | return; | ||
79 | entry->oldptr = old; | ||
80 | entry->rcu_pending = 1; | ||
81 | /* write rcu_pending before calling the RCU callback */ | ||
82 | smp_wmb(); | ||
83 | #ifdef CONFIG_PREEMPT_RCU | ||
84 | synchronize_sched(); /* Until we have the call_rcu_sched() */ | ||
85 | #endif | ||
86 | call_rcu(&entry->rcu, free_old_closure); | ||
87 | } | ||
88 | |||
89 | static void debug_print_probes(struct tracepoint_entry *entry) | ||
90 | { | ||
91 | int i; | ||
92 | |||
93 | if (!tracepoint_debug) | ||
94 | return; | ||
95 | |||
96 | for (i = 0; entry->funcs[i]; i++) | ||
97 | printk(KERN_DEBUG "Probe %d : %p\n", i, entry->funcs[i]); | ||
98 | } | ||
99 | |||
100 | static void * | ||
101 | tracepoint_entry_add_probe(struct tracepoint_entry *entry, void *probe) | ||
102 | { | ||
103 | int nr_probes = 0; | ||
104 | void **old, **new; | ||
105 | |||
106 | WARN_ON(!probe); | ||
107 | |||
108 | debug_print_probes(entry); | ||
109 | old = entry->funcs; | ||
110 | if (old) { | ||
111 | /* (N -> N+1), (N != 0, 1) probes */ | ||
112 | for (nr_probes = 0; old[nr_probes]; nr_probes++) | ||
113 | if (old[nr_probes] == probe) | ||
114 | return ERR_PTR(-EEXIST); | ||
115 | } | ||
116 | /* + 2 : one for new probe, one for NULL func */ | ||
117 | new = kzalloc((nr_probes + 2) * sizeof(void *), GFP_KERNEL); | ||
118 | if (new == NULL) | ||
119 | return ERR_PTR(-ENOMEM); | ||
120 | if (old) | ||
121 | memcpy(new, old, nr_probes * sizeof(void *)); | ||
122 | new[nr_probes] = probe; | ||
123 | entry->refcount = nr_probes + 1; | ||
124 | entry->funcs = new; | ||
125 | debug_print_probes(entry); | ||
126 | return old; | ||
127 | } | ||
128 | |||
129 | static void * | ||
130 | tracepoint_entry_remove_probe(struct tracepoint_entry *entry, void *probe) | ||
131 | { | ||
132 | int nr_probes = 0, nr_del = 0, i; | ||
133 | void **old, **new; | ||
134 | |||
135 | old = entry->funcs; | ||
136 | |||
137 | debug_print_probes(entry); | ||
138 | /* (N -> M), (N > 1, M >= 0) probes */ | ||
139 | for (nr_probes = 0; old[nr_probes]; nr_probes++) { | ||
140 | if ((!probe || old[nr_probes] == probe)) | ||
141 | nr_del++; | ||
142 | } | ||
143 | |||
144 | if (nr_probes - nr_del == 0) { | ||
145 | /* N -> 0, (N > 1) */ | ||
146 | entry->funcs = NULL; | ||
147 | entry->refcount = 0; | ||
148 | debug_print_probes(entry); | ||
149 | return old; | ||
150 | } else { | ||
151 | int j = 0; | ||
152 | /* N -> M, (N > 1, M > 0) */ | ||
153 | /* + 1 for NULL */ | ||
154 | new = kzalloc((nr_probes - nr_del + 1) | ||
155 | * sizeof(void *), GFP_KERNEL); | ||
156 | if (new == NULL) | ||
157 | return ERR_PTR(-ENOMEM); | ||
158 | for (i = 0; old[i]; i++) | ||
159 | if ((probe && old[i] != probe)) | ||
160 | new[j++] = old[i]; | ||
161 | entry->refcount = nr_probes - nr_del; | ||
162 | entry->funcs = new; | ||
163 | } | ||
164 | debug_print_probes(entry); | ||
165 | return old; | ||
166 | } | ||
167 | |||
168 | /* | ||
169 | * Get tracepoint if the tracepoint is present in the tracepoint hash table. | ||
170 | * Must be called with tracepoints_mutex held. | ||
171 | * Returns NULL if not present. | ||
172 | */ | ||
173 | static struct tracepoint_entry *get_tracepoint(const char *name) | ||
174 | { | ||
175 | struct hlist_head *head; | ||
176 | struct hlist_node *node; | ||
177 | struct tracepoint_entry *e; | ||
178 | u32 hash = jhash(name, strlen(name), 0); | ||
179 | |||
180 | head = &tracepoint_table[hash & ((1 << TRACEPOINT_HASH_BITS)-1)]; | ||
181 | hlist_for_each_entry(e, node, head, hlist) { | ||
182 | if (!strcmp(name, e->name)) | ||
183 | return e; | ||
184 | } | ||
185 | return NULL; | ||
186 | } | ||
187 | |||
188 | /* | ||
189 | * Add the tracepoint to the tracepoint hash table. Must be called with | ||
190 | * tracepoints_mutex held. | ||
191 | */ | ||
192 | static struct tracepoint_entry *add_tracepoint(const char *name) | ||
193 | { | ||
194 | struct hlist_head *head; | ||
195 | struct hlist_node *node; | ||
196 | struct tracepoint_entry *e; | ||
197 | size_t name_len = strlen(name) + 1; | ||
198 | u32 hash = jhash(name, name_len-1, 0); | ||
199 | |||
200 | head = &tracepoint_table[hash & ((1 << TRACEPOINT_HASH_BITS)-1)]; | ||
201 | hlist_for_each_entry(e, node, head, hlist) { | ||
202 | if (!strcmp(name, e->name)) { | ||
203 | printk(KERN_NOTICE | ||
204 | "tracepoint %s busy\n", name); | ||
205 | return ERR_PTR(-EEXIST); /* Already there */ | ||
206 | } | ||
207 | } | ||
208 | /* | ||
209 | * Using kmalloc here to allocate a variable length element. Could | ||
210 | * cause some memory fragmentation if overused. | ||
211 | */ | ||
212 | e = kmalloc(sizeof(struct tracepoint_entry) + name_len, GFP_KERNEL); | ||
213 | if (!e) | ||
214 | return ERR_PTR(-ENOMEM); | ||
215 | memcpy(&e->name[0], name, name_len); | ||
216 | e->funcs = NULL; | ||
217 | e->refcount = 0; | ||
218 | e->rcu_pending = 0; | ||
219 | hlist_add_head(&e->hlist, head); | ||
220 | return e; | ||
221 | } | ||
222 | |||
223 | /* | ||
224 | * Remove the tracepoint from the tracepoint hash table. Must be called with | ||
225 | * mutex_lock held. | ||
226 | */ | ||
227 | static int remove_tracepoint(const char *name) | ||
228 | { | ||
229 | struct hlist_head *head; | ||
230 | struct hlist_node *node; | ||
231 | struct tracepoint_entry *e; | ||
232 | int found = 0; | ||
233 | size_t len = strlen(name) + 1; | ||
234 | u32 hash = jhash(name, len-1, 0); | ||
235 | |||
236 | head = &tracepoint_table[hash & ((1 << TRACEPOINT_HASH_BITS)-1)]; | ||
237 | hlist_for_each_entry(e, node, head, hlist) { | ||
238 | if (!strcmp(name, e->name)) { | ||
239 | found = 1; | ||
240 | break; | ||
241 | } | ||
242 | } | ||
243 | if (!found) | ||
244 | return -ENOENT; | ||
245 | if (e->refcount) | ||
246 | return -EBUSY; | ||
247 | hlist_del(&e->hlist); | ||
248 | /* Make sure the call_rcu has been executed */ | ||
249 | if (e->rcu_pending) | ||
250 | rcu_barrier(); | ||
251 | kfree(e); | ||
252 | return 0; | ||
253 | } | ||
254 | |||
255 | /* | ||
256 | * Sets the probe callback corresponding to one tracepoint. | ||
257 | */ | ||
258 | static void set_tracepoint(struct tracepoint_entry **entry, | ||
259 | struct tracepoint *elem, int active) | ||
260 | { | ||
261 | WARN_ON(strcmp((*entry)->name, elem->name) != 0); | ||
262 | |||
263 | /* | ||
264 | * rcu_assign_pointer has a smp_wmb() which makes sure that the new | ||
265 | * probe callbacks array is consistent before setting a pointer to it. | ||
266 | * This array is referenced by __DO_TRACE from | ||
267 | * include/linux/tracepoints.h. A matching smp_read_barrier_depends() | ||
268 | * is used. | ||
269 | */ | ||
270 | rcu_assign_pointer(elem->funcs, (*entry)->funcs); | ||
271 | elem->state = active; | ||
272 | } | ||
273 | |||
274 | /* | ||
275 | * Disable a tracepoint and its probe callback. | ||
276 | * Note: only waiting an RCU period after setting elem->call to the empty | ||
277 | * function insures that the original callback is not used anymore. This insured | ||
278 | * by preempt_disable around the call site. | ||
279 | */ | ||
280 | static void disable_tracepoint(struct tracepoint *elem) | ||
281 | { | ||
282 | elem->state = 0; | ||
283 | } | ||
284 | |||
285 | /** | ||
286 | * tracepoint_update_probe_range - Update a probe range | ||
287 | * @begin: beginning of the range | ||
288 | * @end: end of the range | ||
289 | * | ||
290 | * Updates the probe callback corresponding to a range of tracepoints. | ||
291 | */ | ||
292 | void tracepoint_update_probe_range(struct tracepoint *begin, | ||
293 | struct tracepoint *end) | ||
294 | { | ||
295 | struct tracepoint *iter; | ||
296 | struct tracepoint_entry *mark_entry; | ||
297 | |||
298 | mutex_lock(&tracepoints_mutex); | ||
299 | for (iter = begin; iter < end; iter++) { | ||
300 | mark_entry = get_tracepoint(iter->name); | ||
301 | if (mark_entry) { | ||
302 | set_tracepoint(&mark_entry, iter, | ||
303 | !!mark_entry->refcount); | ||
304 | } else { | ||
305 | disable_tracepoint(iter); | ||
306 | } | ||
307 | } | ||
308 | mutex_unlock(&tracepoints_mutex); | ||
309 | } | ||
310 | |||
311 | /* | ||
312 | * Update probes, removing the faulty probes. | ||
313 | */ | ||
314 | static void tracepoint_update_probes(void) | ||
315 | { | ||
316 | /* Core kernel tracepoints */ | ||
317 | tracepoint_update_probe_range(__start___tracepoints, | ||
318 | __stop___tracepoints); | ||
319 | /* tracepoints in modules. */ | ||
320 | module_update_tracepoints(); | ||
321 | } | ||
322 | |||
323 | /** | ||
324 | * tracepoint_probe_register - Connect a probe to a tracepoint | ||
325 | * @name: tracepoint name | ||
326 | * @probe: probe handler | ||
327 | * | ||
328 | * Returns 0 if ok, error value on error. | ||
329 | * The probe address must at least be aligned on the architecture pointer size. | ||
330 | */ | ||
331 | int tracepoint_probe_register(const char *name, void *probe) | ||
332 | { | ||
333 | struct tracepoint_entry *entry; | ||
334 | int ret = 0; | ||
335 | void *old; | ||
336 | |||
337 | mutex_lock(&tracepoints_mutex); | ||
338 | entry = get_tracepoint(name); | ||
339 | if (!entry) { | ||
340 | entry = add_tracepoint(name); | ||
341 | if (IS_ERR(entry)) { | ||
342 | ret = PTR_ERR(entry); | ||
343 | goto end; | ||
344 | } | ||
345 | } | ||
346 | /* | ||
347 | * If we detect that a call_rcu is pending for this tracepoint, | ||
348 | * make sure it's executed now. | ||
349 | */ | ||
350 | if (entry->rcu_pending) | ||
351 | rcu_barrier(); | ||
352 | old = tracepoint_entry_add_probe(entry, probe); | ||
353 | if (IS_ERR(old)) { | ||
354 | ret = PTR_ERR(old); | ||
355 | goto end; | ||
356 | } | ||
357 | mutex_unlock(&tracepoints_mutex); | ||
358 | tracepoint_update_probes(); /* may update entry */ | ||
359 | mutex_lock(&tracepoints_mutex); | ||
360 | entry = get_tracepoint(name); | ||
361 | WARN_ON(!entry); | ||
362 | tracepoint_entry_free_old(entry, old); | ||
363 | end: | ||
364 | mutex_unlock(&tracepoints_mutex); | ||
365 | return ret; | ||
366 | } | ||
367 | EXPORT_SYMBOL_GPL(tracepoint_probe_register); | ||
368 | |||
369 | /** | ||
370 | * tracepoint_probe_unregister - Disconnect a probe from a tracepoint | ||
371 | * @name: tracepoint name | ||
372 | * @probe: probe function pointer | ||
373 | * | ||
374 | * We do not need to call a synchronize_sched to make sure the probes have | ||
375 | * finished running before doing a module unload, because the module unload | ||
376 | * itself uses stop_machine(), which insures that every preempt disabled section | ||
377 | * have finished. | ||
378 | */ | ||
379 | int tracepoint_probe_unregister(const char *name, void *probe) | ||
380 | { | ||
381 | struct tracepoint_entry *entry; | ||
382 | void *old; | ||
383 | int ret = -ENOENT; | ||
384 | |||
385 | mutex_lock(&tracepoints_mutex); | ||
386 | entry = get_tracepoint(name); | ||
387 | if (!entry) | ||
388 | goto end; | ||
389 | if (entry->rcu_pending) | ||
390 | rcu_barrier(); | ||
391 | old = tracepoint_entry_remove_probe(entry, probe); | ||
392 | mutex_unlock(&tracepoints_mutex); | ||
393 | tracepoint_update_probes(); /* may update entry */ | ||
394 | mutex_lock(&tracepoints_mutex); | ||
395 | entry = get_tracepoint(name); | ||
396 | if (!entry) | ||
397 | goto end; | ||
398 | tracepoint_entry_free_old(entry, old); | ||
399 | remove_tracepoint(name); /* Ignore busy error message */ | ||
400 | ret = 0; | ||
401 | end: | ||
402 | mutex_unlock(&tracepoints_mutex); | ||
403 | return ret; | ||
404 | } | ||
405 | EXPORT_SYMBOL_GPL(tracepoint_probe_unregister); | ||
406 | |||
407 | /** | ||
408 | * tracepoint_get_iter_range - Get a next tracepoint iterator given a range. | ||
409 | * @tracepoint: current tracepoints (in), next tracepoint (out) | ||
410 | * @begin: beginning of the range | ||
411 | * @end: end of the range | ||
412 | * | ||
413 | * Returns whether a next tracepoint has been found (1) or not (0). | ||
414 | * Will return the first tracepoint in the range if the input tracepoint is | ||
415 | * NULL. | ||
416 | */ | ||
417 | int tracepoint_get_iter_range(struct tracepoint **tracepoint, | ||
418 | struct tracepoint *begin, struct tracepoint *end) | ||
419 | { | ||
420 | if (!*tracepoint && begin != end) { | ||
421 | *tracepoint = begin; | ||
422 | return 1; | ||
423 | } | ||
424 | if (*tracepoint >= begin && *tracepoint < end) | ||
425 | return 1; | ||
426 | return 0; | ||
427 | } | ||
428 | EXPORT_SYMBOL_GPL(tracepoint_get_iter_range); | ||
429 | |||
430 | static void tracepoint_get_iter(struct tracepoint_iter *iter) | ||
431 | { | ||
432 | int found = 0; | ||
433 | |||
434 | /* Core kernel tracepoints */ | ||
435 | if (!iter->module) { | ||
436 | found = tracepoint_get_iter_range(&iter->tracepoint, | ||
437 | __start___tracepoints, __stop___tracepoints); | ||
438 | if (found) | ||
439 | goto end; | ||
440 | } | ||
441 | /* tracepoints in modules. */ | ||
442 | found = module_get_iter_tracepoints(iter); | ||
443 | end: | ||
444 | if (!found) | ||
445 | tracepoint_iter_reset(iter); | ||
446 | } | ||
447 | |||
448 | void tracepoint_iter_start(struct tracepoint_iter *iter) | ||
449 | { | ||
450 | tracepoint_get_iter(iter); | ||
451 | } | ||
452 | EXPORT_SYMBOL_GPL(tracepoint_iter_start); | ||
453 | |||
454 | void tracepoint_iter_next(struct tracepoint_iter *iter) | ||
455 | { | ||
456 | iter->tracepoint++; | ||
457 | /* | ||
458 | * iter->tracepoint may be invalid because we blindly incremented it. | ||
459 | * Make sure it is valid by marshalling on the tracepoints, getting the | ||
460 | * tracepoints from following modules if necessary. | ||
461 | */ | ||
462 | tracepoint_get_iter(iter); | ||
463 | } | ||
464 | EXPORT_SYMBOL_GPL(tracepoint_iter_next); | ||
465 | |||
466 | void tracepoint_iter_stop(struct tracepoint_iter *iter) | ||
467 | { | ||
468 | } | ||
469 | EXPORT_SYMBOL_GPL(tracepoint_iter_stop); | ||
470 | |||
471 | void tracepoint_iter_reset(struct tracepoint_iter *iter) | ||
472 | { | ||
473 | iter->module = NULL; | ||
474 | iter->tracepoint = NULL; | ||
475 | } | ||
476 | EXPORT_SYMBOL_GPL(tracepoint_iter_reset); | ||