The marker activation functions sits in kernel/marker.c. A hash table is used to keep track of the registered probes and armed markers, so the markers within a newly loaded module that should be active can be activated at module load time. marker_query has been removed. marker_get_first, marker_get_next and marker_release should be used as iterators on the markers. Signed-off-by: Mathieu Desnoyers --- include/asm-generic/vmlinux.lds.h | 11 include/linux/marker.h | 136 ++++++++ include/linux/module.h | 5 kernel/marker.c | 636 ++++++++++++++++++++++++++++++++++++++ kernel/module.c | 19 + 5 files changed, 806 insertions(+), 1 deletion(-) Index: linux-2.6-lttng/include/asm-generic/vmlinux.lds.h =================================================================== --- linux-2.6-lttng.orig/include/asm-generic/vmlinux.lds.h 2007-07-13 09:08:46.000000000 -0400 +++ linux-2.6-lttng/include/asm-generic/vmlinux.lds.h 2007-07-13 10:32:40.000000000 -0400 @@ -12,7 +12,11 @@ /* .data section */ #define DATA_DATA \ *(.data) \ - *(.data.init.refok) + *(.data.init.refok) \ + . = ALIGN(8); \ + VMLINUX_SYMBOL(__start___markers) = .; \ + *(__markers) \ + VMLINUX_SYMBOL(__stop___markers) = .; #define RO_DATA(align) \ . = ALIGN((align)); \ @@ -129,6 +133,11 @@ VMLINUX_SYMBOL(__stop___immediate) = .; \ } \ \ + /* Markers: strings */ \ + __markers_strings : AT(ADDR(__markers_strings) - LOAD_OFFSET) { \ + *(__markers_strings) \ + } \ + \ /* Kernel symbol table: strings */ \ __ksymtab_strings : AT(ADDR(__ksymtab_strings) - LOAD_OFFSET) { \ *(__ksymtab_strings) \ Index: linux-2.6-lttng/include/linux/marker.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6-lttng/include/linux/marker.h 2007-07-13 10:33:58.000000000 -0400 @@ -0,0 +1,136 @@ +#ifndef _LINUX_MARKER_H +#define _LINUX_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__ + +#include +#include + +struct module; +struct __mark_marker; + +typedef void marker_probe_func(const struct __mark_marker *mdata, + const char *fmt, ...); + +struct __mark_marker { + const char *name; /* Marker name */ + const char *format; /* Marker format string, describing the + * variable argument list. + */ + const char *args; /* List of arguments litteraly transformed + * into a string: "arg1, arg2, arg3". + */ + immediate_char_t state; /* Immediate value state. */ + marker_probe_func *call;/* Probe handler function pointer */ + void *pdata; /* Private probe data */ +} __attribute__((aligned(8))); + +#ifdef CONFIG_MARKERS + +/* + * Generic marker flavor always available. + * Note : the empty asm volatile with read constraint is used here instead of a + * "used" attribute to fix a gcc 4.1.x bug. + * Make sure the alignment of the structure in the __markers section will + * not add unwanted padding between the beginning of the section and the + * structure. Force alignment to the same alignment as the section start. + */ +#define __trace_mark(generic, name, format, args...) \ + do { \ + static const char __mstrtab_name_##name[] \ + __attribute__((section("__markers_strings"))) \ + = #name; \ + static const char __mstrtab_format_##name[] \ + __attribute__((section("__markers_strings"))) \ + = format; \ + static const char __mstrtab_args_##name[] \ + __attribute__((section("__markers_strings"))) \ + = #args; \ + static struct __mark_marker __mark_##name \ + __attribute__((section("__markers"))) = \ + { __mstrtab_name_##name, __mstrtab_format_##name, \ + __mstrtab_args_##name, { 0 }, \ + __mark_empty_function, NULL }; \ + asm volatile ( "" : : "i" (&__mark_##name)); \ + __mark_check_format(format, ## args); \ + if (!generic) { \ + immediate_if (&__mark_##name.state) { \ + preempt_disable(); \ + (*__mark_##name.call) \ + (&__mark_##name, format, ## args); \ + preempt_enable(); \ + } \ + } else { \ + _immediate_if (&__mark_##name.state) { \ + preempt_disable(); \ + (*__mark_##name.call) \ + (&__mark_##name, format, ## args); \ + preempt_enable(); \ + } \ + } \ + } while (0) + +extern void module_marker_update(struct module *mod); +#else /* !CONFIG_MARKERS */ +#define __trace_mark(generic, name, format, args...) \ + __mark_check_format(format, ## args) +static inline void module_marker_update(struct module *mod) { } +#endif /* CONFIG_MARKERS */ + +/* Marker with default behavior */ +#define trace_mark(name, format, args...) \ + __trace_mark(0, name, format, ## args) +/* + * Map to the generic marker. Should be used for markers in __init and __exit + * functions and in lockdep code. + */ +#define _trace_mark(name, format, args...) \ + __trace_mark(1, name, format, ## args) + +#define MARK_MAX_FORMAT_LEN 1024 +/* Pass this as a format string for a marker with no argument */ +#define MARK_NOARGS " " + +/* To be used for string format validity checking with gcc */ +static inline void __mark_check_format(const char *fmt, ...) + __attribute__ ((format (printf, 1, 2))); +static inline void __mark_check_format(const char *fmt, ...) { } + +extern marker_probe_func __mark_empty_function; + +/* + * Connect a probe to a markers. + * pdata must be a valid allocated memory address, or NULL. + */ +extern int marker_probe_register(const char *name, const char *format, + marker_probe_func *probe, void *pdata); + +/* + * Returns the pdata given to marker_probe_register. + */ +extern void *marker_probe_unregister(const char *name); +/* + * Unregister a marker by providing the registered pdata. + */ +extern void *marker_probe_unregister_pdata(void *pdata); + +extern int marker_arm(const char *name); +extern int marker_disarm(const char *name); +extern struct __mark_marker *marker_get_first(void); +extern struct __mark_marker *marker_get_next(struct __mark_marker *iter); +extern void marker_release(struct __mark_marker *iter); +extern void *marker_get_pdata(const char *name); + +#endif /* __KERNEL__ */ +#endif Index: linux-2.6-lttng/include/linux/module.h =================================================================== --- linux-2.6-lttng.orig/include/linux/module.h 2007-07-13 09:08:46.000000000 -0400 +++ linux-2.6-lttng/include/linux/module.h 2007-07-13 10:33:14.000000000 -0400 @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -380,6 +381,10 @@ const struct __immediate *immediate; unsigned int num_immediate; #endif +#ifdef CONFIG_MARKERS + struct __mark_marker *markers; + unsigned int num_markers; +#endif }; #ifndef MODULE_ARCH_INIT #define MODULE_ARCH_INIT {} Index: linux-2.6-lttng/kernel/module.c =================================================================== --- linux-2.6-lttng.orig/kernel/module.c 2007-07-13 09:08:46.000000000 -0400 +++ linux-2.6-lttng/kernel/module.c 2007-07-13 10:33:14.000000000 -0400 @@ -1625,6 +1625,8 @@ unsigned int unusedgplindex; unsigned int unusedgplcrcindex; unsigned int immediateindex = 0; + unsigned int markersindex = 0; + unsigned int markersstringsindex = 0; struct module *mod; long err = 0; void *percpu = NULL, *ptr = NULL; /* Stops spurious gcc warning */ @@ -1724,6 +1726,10 @@ #ifdef CONFIG_IMMEDIATE immediateindex = find_sec(hdr, sechdrs, secstrings, "__immediate"); #endif +#ifdef CONFIG_MARKERS + markersindex = find_sec(hdr, sechdrs, secstrings, "__markers"); + markersstringsindex = find_sec(hdr, sechdrs, secstrings, "__markers_strings"); +#endif /* Don't keep modinfo section */ sechdrs[infoindex].sh_flags &= ~(unsigned long)SHF_ALLOC; @@ -1883,6 +1889,10 @@ sechdrs[immediateindex].sh_size / sizeof(*mod->immediate); } #endif + if (markersindex) + sechdrs[markersindex].sh_flags |= SHF_ALLOC; + if (markersstringsindex) + sechdrs[markersstringsindex].sh_flags |= SHF_ALLOC; mod->unused_syms = (void *)sechdrs[unusedindex].sh_addr; if (unusedcrcindex) @@ -1924,6 +1934,13 @@ if (err < 0) goto cleanup; } +#ifdef CONFIG_MARKERS + if (markersindex) { + mod->markers = (void *)sechdrs[markersindex].sh_addr; + mod->num_markers = + sechdrs[markersindex].sh_size / sizeof(*mod->markers); + } +#endif /* Find duplicate symbols */ err = verify_export_symbols(mod); @@ -1948,6 +1965,8 @@ } #endif + module_marker_update(mod); + module_immediate_setup(mod); err = module_finalize(hdr, sechdrs, mod); Index: linux-2.6-lttng/kernel/marker.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6-lttng/kernel/marker.c 2007-07-13 14:04:02.000000000 -0400 @@ -0,0 +1,636 @@ +/* + * Copyright (C) 2007 Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern struct __mark_marker __start___markers[]; +extern struct __mark_marker __stop___markers[]; + +/* + * module_mutex nests inside markers_mutex. Markers mutex protects the builtin + * and module markers, and the hash table. + */ +DEFINE_MUTEX(markers_mutex); + +/* + * Marker hash table, containing the active markers. + * Protected by module_mutex. + */ +#define MARKER_HASH_BITS 6 +#define MARKER_TABLE_SIZE (1 << MARKER_HASH_BITS) + +struct marker_entry { + struct hlist_node hlist; + char *format; + marker_probe_func *probe; + void *pdata; + int refcount; /* Number of times armed. 0 if disarmed. */ + char name[0]; /* Contains name'\0'format'\0' */ +}; + +static struct hlist_head marker_table[MARKER_TABLE_SIZE]; + +/* + * Empty callback provided as a probe to the markers. By providing this to a + * disabled marker, we makes sure the execution flow is always valid even + * though the function pointer change and the marker enabling are two distinct + * operations that modifies the execution flow of preemptible code. + */ +void __mark_empty_function(const struct __mark_marker *mdata, + const char *fmt, ...) +{ } +EXPORT_SYMBOL_GPL(__mark_empty_function); + +/* + * Get marker if the marker is present in the marker hash table. + * Must be called with markers_mutex held. + * Returns NULL if not present. + */ +static struct marker_entry *_get_marker(const char *name) +{ + struct hlist_head *head; + struct hlist_node *node; + struct marker_entry *e; + size_t len = strlen(name) + 1; + u32 hash = jhash(name, len-1, 0); + + head = &marker_table[hash & ((1 << MARKER_HASH_BITS)-1)]; + hlist_for_each_entry(e, node, head, hlist) { + if (!strcmp(name, e->name)) + return e; + } + return NULL; +} + +/* + * Add the marker to the marker hash table. Must be called with markers_mutex + * held. + */ +static int _add_marker(const char *name, + const char *format, marker_probe_func *probe, void *pdata) +{ + struct hlist_head *head; + struct hlist_node *node; + struct marker_entry *e; + size_t name_len = strlen(name) + 1; + size_t format_len = 0; + u32 hash = jhash(name, name_len-1, 0); + + if (format) + format_len = strlen(format) + 1; + head = &marker_table[hash & ((1 << MARKER_HASH_BITS)-1)]; + hlist_for_each_entry(e, node, head, hlist) { + if (!strcmp(name, e->name)) { + printk(KERN_NOTICE + "Marker %s busy, probe %p already installed\n", + name, e->probe); + return -EBUSY; /* Already there */ + } + } + /* + * Using kmalloc here to allocate a variable length element. Could + * cause some memory fragmentation if overused. + */ + e = kmalloc(sizeof(struct marker_entry) + name_len + format_len, + GFP_KERNEL); + if (!e) + return -ENOMEM; + memcpy(&e->name[0], name, name_len); + if (format) { + e->format = &e->name[name_len]; + memcpy(e->format, format, format_len); + trace_mark(core_marker_format, "name %s format %s", + e->name, e->format); + } else + e->format = NULL; + e->probe = probe; + e->pdata = pdata; + e->refcount = 0; + hlist_add_head(&e->hlist, head); + return 0; +} + +/* + * Remove the marker from the marker hash table. Must be called with mutex_lock + * held. + */ +static void *_remove_marker(const char *name) +{ + struct hlist_head *head; + struct hlist_node *node; + struct marker_entry *e; + int found = 0; + size_t len = strlen(name) + 1; + void *pdata = NULL; + u32 hash = jhash(name, len-1, 0); + + head = &marker_table[hash & ((1 << MARKER_HASH_BITS)-1)]; + hlist_for_each_entry(e, node, head, hlist) { + if (!strcmp(name, e->name)) { + found = 1; + break; + } + } + if (found) { + pdata = e->pdata; + hlist_del(&e->hlist); + kfree(e); + } + return pdata; +} + +/* + * Set the mark_entry format to the format found in the element. + */ +static int _marker_set_format(struct marker_entry **entry, const char *format) +{ + struct marker_entry *e; + size_t name_len = strlen((*entry)->name) + 1; + size_t format_len = strlen(format) + 1; + + e = kmalloc(sizeof(struct marker_entry) + name_len + format_len, + GFP_KERNEL); + if (!e) + return -ENOMEM; + memcpy(&e->name[0], (*entry)->name, name_len); + e->format = &e->name[name_len]; + memcpy(e->format, format, format_len); + e->probe = (*entry)->probe; + e->pdata = (*entry)->pdata; + e->refcount = (*entry)->refcount; + hlist_add_before(&e->hlist, &(*entry)->hlist); + hlist_del(&(*entry)->hlist); + kfree(*entry); + *entry = e; + trace_mark(core_marker_format, "name %s format %s", + e->name, e->format); + return 0; +} + +/* Sets the probe callback corresponding to one marker. */ +static int _set_marker(struct marker_entry **entry, + struct __mark_marker *elem) +{ + int ret; + BUG_ON(strcmp((*entry)->name, elem->name) != 0); + + if ((*entry)->format) { + if (strcmp((*entry)->format, elem->format) != 0) { + printk(KERN_NOTICE + "Format mismatch for probe %s " + "(%s), marker (%s)\n", + (*entry)->name, + (*entry)->format, + elem->format); + return -EPERM; + } + } else { + ret = _marker_set_format(entry, elem->format); + if (ret) + return ret; + } + elem->call = (*entry)->probe; + elem->pdata = (*entry)->pdata; + _immediate_set(&elem->state, 1); + return 0; +} + +/* + * Disable a marker and its probe callback. + * Note: only after a synchronize_sched() issued after setting elem->call to the + * empty function insures that the original callback is not used anymore. This + * insured by preemption disabling around the call site. + */ +static void _disable_marker(struct __mark_marker *elem) +{ + _immediate_set(&elem->state, 0); + elem->call = __mark_empty_function; + /* + * Leave the pdata and id there, because removal is racy and should be + * done only after a synchronize_sched(). There are never used until + * the next initialization anyway. + */ +} + +/* + * Updates the probe callback corresponding to a range of markers. + * Must be called with markers_mutex held. + */ +static void _marker_update_probe_range( + struct __mark_marker *begin, + struct __mark_marker *end, + struct module *probe_module, + int *refcount) +{ + struct __mark_marker *iter; + struct marker_entry *mark_entry; + + for (iter = begin; iter < end; iter++) { + mark_entry = _get_marker(iter->name); + if (mark_entry && mark_entry->refcount) { + _set_marker(&mark_entry, iter); + /* + * ignore error, continue + */ + if (probe_module) + if (probe_module == + __module_text_address((unsigned long)mark_entry->probe)) + (*refcount)++; + } else { + _disable_marker(iter); + } + } +} + +#ifdef CONFIG_MODULES +/* + * Update module probes. + * Must be called with markers_mutex held. + */ +static inline void __marker_update_probes_modules(struct module *probe_module, + int *refcount) +{ + struct module *mod; + + list_for_each_entry(mod, &modules, list) { + if (!mod->taints) { + _marker_update_probe_range(mod->markers, + mod->markers+mod->num_markers, + probe_module, refcount); + } + } +} +#else +static inline void __marker_update_probes_modules(struct module *probe_module, + int *refcount) +{ } +#endif + +/* + * Update probes, removing the faulty probes. + * Issues a synchronize_sched() when no reference to the module passed + * as parameter is found in the probes so the probe module can be + * safely unloaded from now on. + */ +static inline void __marker_update_probes(struct module *probe_module) +{ + int refcount = 0; + + /* Core kernel markers */ + _marker_update_probe_range(__start___markers, + __stop___markers, probe_module, &refcount); + /* Markers in modules. */ + __marker_update_probes_modules(probe_module, &refcount); + if (probe_module && refcount == 0) + synchronize_sched(); +} + +#ifdef CONFIG_MODULES +/* + * Setup the marker according to the data present in the marker hash table + * upon module load. If an error occur during the set probe range, + * refuse to load the module. Must be called with module_mutex held. + * Since the probe_module parameter is NULL, it is safe for refcount to be NULL. + */ +void module_marker_update(struct module *mod) +{ + if (!mod->taints) + _marker_update_probe_range(mod->markers, + mod->markers+mod->num_markers, NULL, NULL); +} + +/* + * Update the system wide probes, with modules. */ +static inline void _marker_update_probes(struct module *probe_module) +{ + mutex_lock(&module_mutex); + __marker_update_probes(probe_module); + mutex_unlock(&module_mutex); +} +#else +/* Update the system wide probes, without modules. */ +static inline void _marker_update_probes(struct module *probe_module) +{ + __marker_update_probes(probe_module); +} +#endif + +/* + * Register a probe : set the callback for each marker. + * Markers must be disarmed to be registered. + */ +int marker_probe_register(const char *name, const char *format, + marker_probe_func *probe, void *pdata) +{ + struct marker_entry *entry; + int ret = 0; + + mutex_lock(&markers_mutex); + entry = _get_marker(name); + if (entry && entry->refcount) { + ret = -EBUSY; + goto end; + } + ret = _add_marker(name, format, probe, pdata); + if (ret) + goto end; + _marker_update_probes(NULL); +end: + mutex_unlock(&markers_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(marker_probe_register); + +/* + * Unregister a probe : unset the callback for each marker. + * returns the pdata if ok. + * else, returns a ERR_PTR(). + */ +void *marker_probe_unregister(const char *name) +{ + struct module *probe_module; + struct marker_entry *entry; + void *pdata; + + mutex_lock(&markers_mutex); + entry = _get_marker(name); + if (!entry) { + pdata = ERR_PTR(-ENOENT); + goto end; + } + entry->refcount = 0; + /* In what module is the probe handler ? */ + probe_module = __module_text_address((unsigned long)entry->probe); + pdata = _remove_marker(name); + _marker_update_probes(probe_module); +end: + mutex_unlock(&markers_mutex); + return pdata; +} +EXPORT_SYMBOL_GPL(marker_probe_unregister); + +/* + * Unregister a probe by pdata : unset the callback for each marker. + * Markers must be disarmed to be unregistered. + * returns the pdata if ok. + * else, returns a ERR_PTR(). + */ +void *marker_probe_unregister_pdata(void *pdata) +{ + struct module *probe_module; + struct hlist_head *head; + struct hlist_node *node; + struct marker_entry *entry; + int found = 0; + unsigned int i; + + mutex_lock(&markers_mutex); + for (i = 0; i < MARKER_TABLE_SIZE; i++) { + head = &marker_table[i]; + hlist_for_each_entry(entry, node, head, hlist) { + if (entry->pdata == pdata) { + found = 1; + goto iter_end; + } + } + } +iter_end: + if (!found) { + pdata = ERR_PTR(-ENOENT); + goto end; + } + entry->refcount = 0; + /* In what module is the probe handler ? */ + probe_module = __module_text_address((unsigned long)entry->probe); + pdata = _remove_marker(entry->name); + _marker_update_probes(probe_module); +end: + mutex_unlock(&markers_mutex); + return pdata; +} +EXPORT_SYMBOL_GPL(marker_probe_unregister_pdata); + +/* + * Arm the probe : arm the immediate values. + * A probe must have been previously registered. + */ +int marker_arm(const char *name) +{ + struct marker_entry * entry; + int ret = 0; + + mutex_lock(&markers_mutex); + entry = _get_marker(name); + if (!entry) { + ret = -ENOENT; + goto end; + } + /* + * Only need to update probes when refcount passes from 0 to 1. + */ + if (entry->refcount++) + goto end; + _marker_update_probes(NULL); +end: + mutex_unlock(&markers_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(marker_arm); + +/* + * Disarm the probe : disarm the immediate and set the empty callback for each + * marker. + */ +int marker_disarm(const char *name) +{ + struct marker_entry * entry; + int ret = 0; + + mutex_lock(&markers_mutex); + entry = _get_marker(name); + if (!entry) { + ret = -ENOENT; + goto end; + } + /* + * Only permit decrement refcount if higher than 0. + * Do probe update only on 1 -> 0 transition. + */ + if (entry->refcount) { + if (--entry->refcount) + goto end; + } else { + ret = -EPERM; + goto end; + } + _marker_update_probes(NULL); +end: + mutex_unlock(&markers_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(marker_disarm); + +void *marker_get_pdata(const char *name) +{ + struct hlist_head *head; + struct hlist_node *node; + struct marker_entry *e; + size_t name_len = strlen(name) + 1; + u32 hash = jhash(name, name_len-1, 0); + int found = 0; + + head = &marker_table[hash & ((1 << MARKER_HASH_BITS)-1)]; + hlist_for_each_entry(e, node, head, hlist) { + if (!strcmp(name, e->name)) { + found = 1; + return e->pdata; + } + } + return ERR_PTR(-ENOENT); +} +EXPORT_SYMBOL_GPL(marker_get_pdata); + + +/* + * No markers are added to the core marker section, no lock needed. + * Must be called with modules mutex held for modules markers. + * Returns 1 if current is found, or 0 if current is not found. + */ +static int _marker_get_next_range(struct __mark_marker *cur, + int found, + struct __mark_marker **next, + struct __mark_marker *begin, + struct __mark_marker *end) +{ + if (found && begin != end) { + /* + * current found in the previous module, get the following + * marker. + */ + *next = begin; + } else if (cur >= begin && cur < end) { + found = 1; + if (cur + 1 < end) { + /* + * next marker sits within the same module. + */ + *next = cur + 1; + } + } + return found; +} + +#ifdef CONFIG_MODULES +/* + * Returns 0 if current not foud. + * Returns 1 if current found. + */ +static inline int marker_get_next_modules(struct __mark_marker *cur, + int found, + struct __mark_marker **next) +{ + struct module *iter_mod; + struct module *cur_mod = NULL; + + mutex_lock(&module_mutex); + list_for_each_entry(iter_mod, &modules, list) { + if (!iter_mod->taints) { + found = _marker_get_next_range(cur, found, next, + iter_mod->markers, + iter_mod->markers + iter_mod->num_markers); + /* + * Stop if the current marker has been found _and_ + * the next marker has been set. Inc module refcount. + * + * If the current marker is found and not the next one, + * remember the current module for module_put. + */ + if (found) { + if (*next) { + __module_get(iter_mod); + break; + } else + cur_mod = iter_mod; + } + + } + } + mutex_unlock(&module_mutex); + if (cur_mod) + module_put(cur_mod); + return found; +} +#else +static inline int marker_get_next_modules(struct __mark_marker *cur, + int found, + struct __mark_marker **next) +{ + return 0; +} +#endif + +/* + * Only need to increment the module use count. + * We allow modification of markers beneath us, since we only want to output + * their information. + * Returns NULL when it reaches the last marker. + */ +struct __mark_marker *marker_get_next(struct __mark_marker *cur) +{ + int found = 0; + struct __mark_marker *next = NULL; + + /* Get the first marker when cur is NULL */ + if (!cur) + found = 1; + /* Core kernel markers */ + found = _marker_get_next_range(cur, found, &next, + __start___markers, __stop___markers); + if (found && next) + goto end; + /* Markers in modules. */ + found = marker_get_next_modules(cur, found, &next); +end: + return next; +} +EXPORT_SYMBOL_GPL(marker_get_next); + +struct __mark_marker *marker_get_first(void) +{ + return marker_get_next(NULL); +} +EXPORT_SYMBOL_GPL(marker_get_first); + +/* + * FIXME: suboptimal + * Gets the markers until the end, so no module refcount is held when the + * function ends. + */ +void marker_release(struct __mark_marker *iter) +{ + while (iter != NULL) + iter = marker_get_next(iter); +} +EXPORT_SYMBOL_GPL(marker_release); -- Mathieu Desnoyers Computer Engineering Ph.D. Student, Ecole Polytechnique de Montreal OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68 - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/