From: Mathieu Desnoyers Linux Kernel Markers, architecture independent code. [bunk@stusta.de: marker exports must be EXPORT_SYMBOL_GPL] Signed-off-by: Mathieu Desnoyers Signed-off-by: Adrian Bunk Signed-off-by: Andrew Morton --- include/asm-generic/vmlinux.lds.h | 13 + include/linux/marker.h | 108 +++++++++++++ include/linux/module.h | 4 kernel/module.c | 231 ++++++++++++++++++++++++++++ 4 files changed, 356 insertions(+) diff -puN include/asm-generic/vmlinux.lds.h~linux-kernel-markers-architecture-independant-code include/asm-generic/vmlinux.lds.h --- a/include/asm-generic/vmlinux.lds.h~linux-kernel-markers-architecture-independant-code +++ a/include/asm-generic/vmlinux.lds.h @@ -121,6 +121,19 @@ __ksymtab_strings : AT(ADDR(__ksymtab_strings) - LOAD_OFFSET) { \ *(__ksymtab_strings) \ } \ + /* Kernel markers : pointers */ \ + .markers : AT(ADDR(.markers) - LOAD_OFFSET) { \ + VMLINUX_SYMBOL(__start___markers) = .; \ + *(.markers) \ + VMLINUX_SYMBOL(__stop___markers) = .; \ + } \ + .markers.c : AT(ADDR(.markers.c) - LOAD_OFFSET) { \ + VMLINUX_SYMBOL(__start___markers_c) = .; \ + *(.markers.c) \ + VMLINUX_SYMBOL(__stop___markers_c) = .; \ + } \ + __end_rodata = .; \ + . = ALIGN(4096); \ \ /* Built-in module parameters. */ \ __param : AT(ADDR(__param) - LOAD_OFFSET) { \ diff -puN /dev/null include/linux/marker.h --- /dev/null +++ a/include/linux/marker.h @@ -0,0 +1,108 @@ +#ifndef _LINUX_MARKER_H +#define _LINUX_MARKER_H + +/* + * marker.h + * + * Code markup for dynamic and static tracing. + * + * See Documentation/marker.txt. + * + * (C) Copyright 2006 Mathieu Desnoyers + * + * This file is released under the GPLv2. + * See the file COPYING for more details. + */ + +#ifdef __KERNEL__ + +typedef void marker_probe_func(const char *fmt, ...); + +struct __mark_marker_c { + const char *name; + marker_probe_func **call; + const char *format; + int flags; +} __attribute__((packed)); + +struct __mark_marker { + const struct __mark_marker_c *cmark; + void *enable; +} __attribute__((packed)); + +/* Generic marker flavor always available */ +#ifdef CONFIG_MARKERS + +#define MF_OPTIMIZED 1 /* Use optimized markers */ +#define MF_LOCKDEP 2 /* Can call lockdep */ +#define MF_PRINTK 3 /* vprintk can be called in the probe */ + +#define _MF_OPTIMIZED (1 << MF_OPTIMIZED) +#define _MF_LOCKDEP (1 << MF_LOCKDEP) +#define _MF_PRINTK (1 << MF_PRINTK) + +#define MARK_GENERIC(flags, name, format, args...) \ + do { \ + static marker_probe_func *__mark_call_##name = \ + __mark_empty_function; \ + static char __marker_enable_##name = 0; \ + static const struct __mark_marker_c __mark_c_##name \ + __attribute__((section(".markers.c"))) = \ + { #name, &__mark_call_##name, format, \ + (flags) | ~_MF_OPTIMIZED } ; \ + static const struct __mark_marker __mark_##name \ + __attribute__((section(".markers"))) = \ + { &__mark_c_##name, &__marker_enable_##name } ; \ + asm volatile ( "" : : "i" (&__mark_##name)); \ + __mark_check_format(format, ## args); \ + if (unlikely(__marker_enable_##name)) { \ + preempt_disable(); \ + (*__mark_call_##name)(format, ## args); \ + preempt_enable(); \ + } \ + } while (0) + +#define MARK_GENERIC_ENABLE_IMMEDIATE_OFFSET 0 +#define MARK_GENERIC_ENABLE_TYPE char +/* Dereference enable as lvalue from a pointer to its instruction */ +#define MARK_GENERIC_ENABLE(a) \ + *(MARK_GENERIC_ENABLE_TYPE*) \ + ((char*)a+MARK_GENERIC_ENABLE_IMMEDIATE_OFFSET) + +static inline int marker_generic_set_enable(void *address, char enable) +{ + MARK_GENERIC_ENABLE(address) = enable; + return 0; +} + +#else /* !CONFIG_MARKERS */ +#define MARK_GENERIC(flags, name, format, args...) \ + __mark_check_format(format, ## args) +#endif /* CONFIG_MARKERS */ + +#ifdef CONFIG_MARKERS_ENABLE_OPTIMIZATION +#include /* optimized marker flavor */ +#else +#include /* fallback on generic markers */ +#endif + +#define MARK_MAX_FORMAT_LEN 1024 +#define MARK_NOARGS " " + +static inline __attribute__ ((format (printf, 1, 2))) +void __mark_check_format(const char *fmt, ...) +{ } + +extern marker_probe_func __mark_empty_function; + +extern int _marker_set_probe(int flags, const char *name, const char *format, + marker_probe_func *probe); + +#define marker_set_probe(name, format, probe) \ + _marker_set_probe(_MF_DEFAULT, name, format, probe) + +extern int marker_remove_probe(marker_probe_func *probe); +extern int marker_list_probe(marker_probe_func *probe); + +#endif /* __KERNEL__ */ +#endif diff -puN include/linux/module.h~linux-kernel-markers-architecture-independant-code include/linux/module.h --- a/include/linux/module.h~linux-kernel-markers-architecture-independant-code +++ a/include/linux/module.h @@ -355,6 +355,9 @@ struct module /* The command line arguments (may be mangled). People like keeping pointers to this stuff */ char *args; + + const struct __mark_marker *markers; + unsigned int num_markers; }; /* FIXME: It'd be nice to isolate modules during init, too, so they @@ -464,6 +467,7 @@ int register_module_notifier(struct noti int unregister_module_notifier(struct notifier_block * nb); extern void print_modules(void); +extern void list_modules(void); #else /* !CONFIG_MODULES... */ #define EXPORT_SYMBOL(sym) diff -puN kernel/module.c~linux-kernel-markers-architecture-independant-code kernel/module.c --- a/kernel/module.c~linux-kernel-markers-architecture-independant-code +++ a/kernel/module.c @@ -139,6 +139,8 @@ extern const unsigned long __start___kcr extern const unsigned long __start___kcrctab_gpl_future[]; extern const unsigned long __start___kcrctab_unused[]; extern const unsigned long __start___kcrctab_unused_gpl[]; +extern const struct __mark_marker __start___markers[]; +extern const struct __mark_marker __stop___markers[]; #ifndef CONFIG_MODVERSIONS #define symversion(base, idx) NULL @@ -299,6 +301,201 @@ static struct module *find_module(const return NULL; } +#ifdef CONFIG_MARKERS +void __mark_empty_function(const char *fmt, ...) +{ +} +EXPORT_SYMBOL_GPL(__mark_empty_function); + +static int marker_set_enable(void *address, char enable, int flags) +{ + if (flags & _MF_OPTIMIZED) + return marker_optimized_set_enable(address, enable); + else + return marker_generic_set_enable(address, enable); +} + +/* enable and function address are set out of order, and it's ok : + * the state is always coherent. */ +static int _marker_set_probe_range(int flags, const char *name, + const char *format, + marker_probe_func *probe, + const struct __mark_marker *begin, + const struct __mark_marker *end) +{ + const struct __mark_marker *iter; + int found = 0; + + for (iter = begin; iter < end; iter++) { + if (strcmp(name, iter->cmark->name) == 0) { + if (format + && strcmp(format, iter->cmark->format) != 0) { + printk(KERN_NOTICE + "Format mismatch for probe %s " + "(%s), marker (%s)\n", + name, + format, + iter->cmark->format); + continue; + } + if (flags & _MF_LOCKDEP + && !(iter->cmark->flags & _MF_LOCKDEP)) { + printk(KERN_NOTICE + "Incompatible lockdep flags for " + "probe %s\n", + name); + continue; + } + if (flags & _MF_PRINTK + && !(iter->cmark->flags & _MF_PRINTK)) { + printk(KERN_NOTICE + "Incompatible printk flags for " + "probe %s\n", + name); + continue; + } + if (probe == __mark_empty_function) { + if (*iter->cmark->call + != __mark_empty_function) { + *iter->cmark->call = + __mark_empty_function; + } + marker_set_enable(iter->enable, 0, + iter->cmark->flags); + } else { + if (*iter->cmark->call + != __mark_empty_function) { + if (*iter->cmark->call != probe) { + printk(KERN_NOTICE + "Marker %s busy, " + "probe %p already " + "installed\n", + name, + *iter->cmark->call); + continue; + } + } else { + found++; + *iter->cmark->call = probe; + } + /* Can have many enables for one function */ + marker_set_enable(iter->enable, 1, + iter->cmark->flags); + } + found++; + } + } + return found; +} + +static int marker_remove_probe_range(marker_probe_func *probe, + const struct __mark_marker *begin, + const struct __mark_marker *end) +{ + const struct __mark_marker *iter; + int found = 0; + + for (iter = begin; iter < end; iter++) { + if (*iter->cmark->call == probe) { + marker_set_enable(iter->enable, 0, + iter->cmark->flags); + *iter->cmark->call = __mark_empty_function; + found++; + } + } + return found; +} + +static int marker_list_probe_range(marker_probe_func *probe, + const struct __mark_marker *begin, + const struct __mark_marker *end) +{ + const struct __mark_marker *iter; + int found = 0; + + for (iter = begin; iter < end; iter++) { + if (probe) + if (probe != *iter->cmark->call) continue; + printk("name %s \n", iter->cmark->name); + if (iter->cmark->flags & _MF_OPTIMIZED) + printk(" enable %u optimized ", + MARK_OPTIMIZED_ENABLE(iter->enable)); + else + printk(" enable %u generic ", + MARK_GENERIC_ENABLE(iter->enable)); + printk(" func 0x%p format \"%s\"\n", + *iter->cmark->call, iter->cmark->format); + found++; + } + return found; +} +/* markers use the modlist_lock to to synchronise */ +int _marker_set_probe(int flags, const char *name, const char *format, + marker_probe_func *probe) +{ + struct module *mod; + int found = 0; + + mutex_lock(&module_mutex); + /* Core kernel markers */ + found += _marker_set_probe_range(flags, name, format, probe, + __start___markers, __stop___markers); + /* Markers in modules. */ + list_for_each_entry(mod, &modules, list) { + if (!mod->taints) + found += _marker_set_probe_range(flags, name, format, + probe, mod->markers, mod->markers+mod->num_markers); + } + mutex_unlock(&module_mutex); + return found; +} +EXPORT_SYMBOL_GPL(_marker_set_probe); + +int marker_remove_probe(marker_probe_func *probe) +{ + struct module *mod; + int found = 0; + + mutex_lock(&module_mutex); + /* Core kernel markers */ + found += marker_remove_probe_range(probe, + __start___markers, __stop___markers); + /* Markers in modules. */ + list_for_each_entry(mod, &modules, list) { + if (!mod->taints) + found += marker_remove_probe_range(probe, + mod->markers, mod->markers+mod->num_markers); + } + mutex_unlock(&module_mutex); + return found; +} +EXPORT_SYMBOL_GPL(marker_remove_probe); + +int marker_list_probe(marker_probe_func *probe) +{ + struct module *mod; + int found = 0; + + mutex_lock(&module_mutex); + /* Core kernel markers */ + printk("Listing kernel markers\n"); + found += marker_list_probe_range(probe, + __start___markers, __stop___markers); + /* Markers in modules. */ + printk("Listing module markers\n"); + list_for_each_entry(mod, &modules, list) { + if (!mod->taints) { + printk("Listing markers for module %s\n", mod->name); + found += marker_list_probe_range(probe, + mod->markers, mod->markers+mod->num_markers); + } + } + mutex_unlock(&module_mutex); + return found; +} +EXPORT_SYMBOL_GPL(marker_list_probe); +#endif + #ifdef CONFIG_SMP /* Number of blocks used and allocated. */ static unsigned int pcpu_num_used, pcpu_num_allocated; @@ -1577,6 +1774,7 @@ static struct module *load_module(void _ unsigned int unusedcrcindex; unsigned int unusedgplindex; unsigned int unusedgplcrcindex; + unsigned int markersindex; struct module *mod; long err = 0; void *percpu = NULL, *ptr = NULL; /* Stops spurious gcc warning */ @@ -1673,6 +1871,7 @@ static struct module *load_module(void _ #ifdef ARCH_UNWIND_SECTION_NAME unwindex = find_sec(hdr, sechdrs, secstrings, ARCH_UNWIND_SECTION_NAME); #endif + markersindex = find_sec(hdr, sechdrs, secstrings, ".markers"); /* Don't keep modinfo section */ sechdrs[infoindex].sh_flags &= ~(unsigned long)SHF_ALLOC; @@ -1683,6 +1882,13 @@ static struct module *load_module(void _ #endif if (unwindex) sechdrs[unwindex].sh_flags |= SHF_ALLOC; +#ifdef CONFIG_MARKERS + if (markersindex) + sechdrs[markersindex].sh_flags |= SHF_ALLOC; +#else + if (markersindex) + sechdrs[markersindex].sh_flags &= ~(unsigned long)SHF_ALLOC; +#endif /* Check module struct version now, before we try to use module. */ if (!check_modstruct_version(sechdrs, versindex, mod)) { @@ -1823,6 +2029,11 @@ static struct module *load_module(void _ mod->gpl_future_syms = (void *)sechdrs[gplfutureindex].sh_addr; if (gplfuturecrcindex) mod->gpl_future_crcs = (void *)sechdrs[gplfuturecrcindex].sh_addr; + if (markersindex) { + mod->markers = (void *)sechdrs[markersindex].sh_addr; + mod->num_markers = + sechdrs[markersindex].sh_size / sizeof(*mod->markers); + } mod->unused_syms = (void *)sechdrs[unusedindex].sh_addr; if (unusedcrcindex) @@ -2263,6 +2474,26 @@ const struct seq_operations modules_op = .show = m_show }; +void list_modules(void) +{ + /* Enumerate loaded modules */ + struct list_head *i; + struct module *mod; + unsigned long refcount = 0; + + mutex_lock(&module_mutex); + list_for_each(i, &modules) { + mod = list_entry(i, struct module, list); +#ifdef CONFIG_MODULE_UNLOAD + refcount = local_read(&mod->ref[0].count); +#endif //CONFIG_MODULE_UNLOAD + MARK(list_modules, "%s %d[enum module_state] %lu", + mod->name, mod->state, refcount); + } + mutex_unlock(&module_mutex); +} +EXPORT_SYMBOL_GPL(list_modules); + /* Given an address, look for it in the module exception tables. */ const struct exception_table_entry *search_module_extables(unsigned long addr) { _