aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/module.c
diff options
context:
space:
mode:
authorMathieu Desnoyers <mathieu.desnoyers@polymtl.ca>2008-07-18 12:16:16 -0400
committerIngo Molnar <mingo@elte.hu>2008-10-14 04:28:28 -0400
commit97e1c18e8d17bd87e1e383b2e9d9fc740332c8e2 (patch)
tree5c6bfce8bacf04c2993a0029788a1370a483afa6 /kernel/module.c
parente7f2f9918c0e97aa98ba147ca387e2c7238f0711 (diff)
tracing: Kernel Tracepoints
Implementation of kernel tracepoints. Inspired from the Linux Kernel Markers. Allows complete typing verification by declaring both tracing statement inline functions and probe registration/unregistration static inline functions within the same macro "DEFINE_TRACE". No format string is required. See the tracepoint Documentation and Samples patches for usage examples. Taken from the documentation patch : "A tracepoint placed in code provides a hook to call a function (probe) that you can provide at runtime. A tracepoint can be "on" (a probe is connected to it) or "off" (no probe is attached). When a tracepoint is "off" it has no effect, except for adding a tiny time penalty (checking a condition for a branch) and space penalty (adding a few bytes for the function call at the end of the instrumented function and adds a data structure in a separate section). When a tracepoint is "on", the function you provide is called each time the tracepoint is executed, in the execution context of the caller. When the function provided ends its execution, it returns to the caller (continuing from the tracepoint site). You can put tracepoints at important locations in the code. They are lightweight hooks that can pass an arbitrary number of parameters, which prototypes are described in a tracepoint declaration placed in a header file." Addition and removal of tracepoints is synchronized by RCU using the scheduler (and preempt_disable) as guarantees to find a quiescent state (this is really RCU "classic"). The update side uses rcu_barrier_sched() with call_rcu_sched() and the read/execute side uses "preempt_disable()/preempt_enable()". We make sure the previous array containing probes, which has been scheduled for deletion by the rcu callback, is indeed freed before we proceed to the next update. It therefore limits the rate of modification of a single tracepoint to one update per RCU period. The objective here is to permit fast batch add/removal of probes on _different_ tracepoints. Changelog : - Use #name ":" #proto as string to identify the tracepoint in the tracepoint table. This will make sure not type mismatch happens due to connexion of a probe with the wrong type to a tracepoint declared with the same name in a different header. - Add tracepoint_entry_free_old. - Change __TO_TRACE to get rid of the 'i' iterator. Masami Hiramatsu <mhiramat@redhat.com> : Tested on x86-64. Performance impact of a tracepoint : same as markers, except that it adds about 70 bytes of instructions in an unlikely branch of each instrumented function (the for loop, the stack setup and the function call). It currently adds a memory read, a test and a conditional branch at the instrumentation site (in the hot path). Immediate values will eventually change this into a load immediate, test and branch, which removes the memory read which will make the i-cache impact smaller (changing the memory read for a load immediate removes 3-4 bytes per site on x86_32 (depending on mov prefixes), or 7-8 bytes on x86_64, it also saves the d-cache hit). About the performance impact of tracepoints (which is comparable to markers), even without immediate values optimizations, tests done by Hideo Aoki on ia64 show no regression. His test case was using hackbench on a kernel where scheduler instrumentation (about 5 events in code scheduler code) was added. Quoting Hideo Aoki about Markers : I evaluated overhead of kernel marker using linux-2.6-sched-fixes git tree, which includes several markers for LTTng, using an ia64 server. While the immediate trace mark feature isn't implemented on ia64, there is no major performance regression. So, I think that we don't have any issues to propose merging marker point patches into Linus's tree from the viewpoint of performance impact. I prepared two kernels to evaluate. The first one was compiled without CONFIG_MARKERS. The second one was enabled CONFIG_MARKERS. I downloaded the original hackbench from the following URL: http://devresources.linux-foundation.org/craiger/hackbench/src/hackbench.c I ran hackbench 5 times in each condition and calculated the average and difference between the kernels. The parameter of hackbench: every 50 from 50 to 800 The number of CPUs of the server: 2, 4, and 8 Below is the results. As you can see, major performance regression wasn't found in any case. Even if number of processes increases, differences between marker-enabled kernel and marker- disabled kernel doesn't increase. Moreover, if number of CPUs increases, the differences doesn't increase either. Curiously, marker-enabled kernel is better than marker-disabled kernel in more than half cases, although I guess it comes from the difference of memory access pattern. * 2 CPUs Number of | without | with | diff | diff | processes | Marker [Sec] | Marker [Sec] | [Sec] | [%] | -------------------------------------------------------------- 50 | 4.811 | 4.872 | +0.061 | +1.27 | 100 | 9.854 | 10.309 | +0.454 | +4.61 | 150 | 15.602 | 15.040 | -0.562 | -3.6 | 200 | 20.489 | 20.380 | -0.109 | -0.53 | 250 | 25.798 | 25.652 | -0.146 | -0.56 | 300 | 31.260 | 30.797 | -0.463 | -1.48 | 350 | 36.121 | 35.770 | -0.351 | -0.97 | 400 | 42.288 | 42.102 | -0.186 | -0.44 | 450 | 47.778 | 47.253 | -0.526 | -1.1 | 500 | 51.953 | 52.278 | +0.325 | +0.63 | 550 | 58.401 | 57.700 | -0.701 | -1.2 | 600 | 63.334 | 63.222 | -0.112 | -0.18 | 650 | 68.816 | 68.511 | -0.306 | -0.44 | 700 | 74.667 | 74.088 | -0.579 | -0.78 | 750 | 78.612 | 79.582 | +0.970 | +1.23 | 800 | 85.431 | 85.263 | -0.168 | -0.2 | -------------------------------------------------------------- * 4 CPUs Number of | without | with | diff | diff | processes | Marker [Sec] | Marker [Sec] | [Sec] | [%] | -------------------------------------------------------------- 50 | 2.586 | 2.584 | -0.003 | -0.1 | 100 | 5.254 | 5.283 | +0.030 | +0.56 | 150 | 8.012 | 8.074 | +0.061 | +0.76 | 200 | 11.172 | 11.000 | -0.172 | -1.54 | 250 | 13.917 | 14.036 | +0.119 | +0.86 | 300 | 16.905 | 16.543 | -0.362 | -2.14 | 350 | 19.901 | 20.036 | +0.135 | +0.68 | 400 | 22.908 | 23.094 | +0.186 | +0.81 | 450 | 26.273 | 26.101 | -0.172 | -0.66 | 500 | 29.554 | 29.092 | -0.461 | -1.56 | 550 | 32.377 | 32.274 | -0.103 | -0.32 | 600 | 35.855 | 35.322 | -0.533 | -1.49 | 650 | 39.192 | 38.388 | -0.804 | -2.05 | 700 | 41.744 | 41.719 | -0.025 | -0.06 | 750 | 45.016 | 44.496 | -0.520 | -1.16 | 800 | 48.212 | 47.603 | -0.609 | -1.26 | -------------------------------------------------------------- * 8 CPUs Number of | without | with | diff | diff | processes | Marker [Sec] | Marker [Sec] | [Sec] | [%] | -------------------------------------------------------------- 50 | 2.094 | 2.072 | -0.022 | -1.07 | 100 | 4.162 | 4.273 | +0.111 | +2.66 | 150 | 6.485 | 6.540 | +0.055 | +0.84 | 200 | 8.556 | 8.478 | -0.078 | -0.91 | 250 | 10.458 | 10.258 | -0.200 | -1.91 | 300 | 12.425 | 12.750 | +0.325 | +2.62 | 350 | 14.807 | 14.839 | +0.032 | +0.22 | 400 | 16.801 | 16.959 | +0.158 | +0.94 | 450 | 19.478 | 19.009 | -0.470 | -2.41 | 500 | 21.296 | 21.504 | +0.208 | +0.98 | 550 | 23.842 | 23.979 | +0.137 | +0.57 | 600 | 26.309 | 26.111 | -0.198 | -0.75 | 650 | 28.705 | 28.446 | -0.259 | -0.9 | 700 | 31.233 | 31.394 | +0.161 | +0.52 | 750 | 34.064 | 33.720 | -0.344 | -1.01 | 800 | 36.320 | 36.114 | -0.206 | -0.57 | -------------------------------------------------------------- Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca> Acked-by: Masami Hiramatsu <mhiramat@redhat.com> Acked-by: 'Peter Zijlstra' <peterz@infradead.org> Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'kernel/module.c')
-rw-r--r--kernel/module.c66
1 files changed, 65 insertions, 1 deletions
diff --git a/kernel/module.c b/kernel/module.c
index 9db11911e04..661d73db786 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
2739void 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 */
2755int 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