Linux Kernel Markers - Support Multiple Probes
PATCH 1/1 Linux Kernel Markers - Support Multiple Probes
RCU style multiple probes support for the Linux Kernel Markers. Common case (one probe) is still fast and does not require dynamic allocation or a supplementary pointer dereference on the fast path.
- Move preempt disable from the marker site to the callback.
Since we now have an internal callback, move the preempt disable/enable to the callback instead of the marker site.
Since the callback change is done asynchronously (passing from a handler that supports arguments to a handler that does not setup the arguments is no arguments are passed), we can safely update it even if it is outside the preempt disable section.
- Move probe arm to probe connection. Now, a connected probe is automatically armed.
Remove MARK_MAX_FORMAT_LEN, unused.
This patch modifies the Linux Kernel Markers API : it removes the probe "arm/disarm" and changes the probe function prototype : it now expects a va_list * instead of a "...".
It applies on top of 2.6.24-rc3-git1.
--- include/linux/marker.h | 59 ++- include/linux/module.h | 2 kernel/marker.c | 671 +++++++++++++++++++++++++++++----------- kernel/module.c | 7 samples/markers/probe-example.c | 25 - 5 files changed, 548 insertions(+), 216 deletions(-)
include/linux/marker.h +36 -23
@@ -19,16 +19,23 @@ struct marker;/** * marker_probe_func - Type of a marker probe function -* @mdata: pointer of type struct marker -* @private_data: caller site private data +* @probe_private: probe private data +* @call_private: call site private data * @fmt: format string -* @...: variable argument list +* @args: variable argument list pointer. Use a pointer to overcome C's +* inability to pass this around as a pointer in a portable manner in +* the callee otherwise. * * Type of marker probe functions. They receive the mdata and need to parse the * format string to recover the variable argument list. */ -typedef void marker_probe_func (const struct marker *mdata, -void *private_data,const char *fmt, ...); +typedef void marker_probe_func (void *probe_private,void *call_private, +const char *fmt,va_list *args); + +struct marker_probe_closure { +marker_probe_func *func;/* Callback */ +void *probe_private;/* Private probe data */ +};struct marker {const char *name;/* Marker name */
@@ -36,8 +43,11 @@ struct marker {* variable argument list. */ char state;/* Marker state. */ -marker_probe_func *call;/* Probe handler function pointer */ -void *private;/* Private probe data */ +char ptype;/* probe type : 0 : single, 1 : multi */ +void (*call)(const struct marker *mdata,/* Probe wrapper */ +void *call_private,const char *fmt, ...); +struct marker_probe_closure single; +struct marker_probe_closure *multi; } __attribute__((aligned(8)));#ifdef CONFIG_MARKERS
@@ -49,7 +59,7 @@ struct marker {* 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(name, call_data, format, args...) \ +#define __trace_mark(name, call_private, format, args...) \ do { \ static const char __mstrtab_name_##name[] \ __attribute__((section("__markers_strings"))) \
@@ -60,24 +70,23 @@ struct marker { static struct marker __mark_##name \ __attribute__((section("__markers"), aligned(8))) = \ { __mstrtab_name_##name, __mstrtab_format_##name, \ - 0, __mark_empty_function, NULL }; \ + 0, 0, marker_probe_cb, \ + { __mark_empty_function, NULL}, NULL }; \ __mark_check_format(format, ## args); \ if (unlikely(__mark_##name.state)) { \ - preempt_disable(); \ (*__mark_##name.call) \ - (&__mark_##name, call_data, \ + (&__mark_##name, call_private, \ format, ## args); \ - preempt_enable(); \ } \ } while (0)extern void marker_update_probe_range (struct marker *begin, -struct marker *end,struct module *probe_module,int *refcount); +struct marker *end);#else /* !CONFIG_MARKERS */ -#define __trace_mark(name, call_data, format, args...) \ +#define __trace_mark(name, call_private, format, args...) \ __mark_check_format(format, ## args)static inline void marker_update_probe_range (struct marker *begin, -struct marker *end,struct module *probe_module,int *refcount) +struct marker *end) { }#endif /* CONFIG_MARKERS */
@@ -92,8 +101,6 @@ static inline void marker_update_probe_r #define trace_mark(name, format, args...) \ __trace_mark(name, NULL, format, ## args) -#define MARK_MAX_FORMAT_LEN 1024 -/** * MARK_NOARGS - Format string for a marker with no argument. */
@@ -106,24 +113,30 @@ static inline void __printf(1, 2) __markextern marker_probe_func __mark_empty_function; +extern void marker_probe_cb (const struct marker *mdata, +void *call_private,const char *fmt, ...); +extern void marker_probe_cb_noarg (const struct marker *mdata, +void *call_private,const char *fmt, ...); +/* * Connect a probe to a marker. * private data pointer 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 *private); +marker_probe_func *probe,void *probe_private);/* * Returns the private data given to marker_probe_register. */ -extern void *marker_probe_unregister (const char *name); +extern int marker_probe_unregister (const char *name, +marker_probe_func *probe,void *probe_private);/* * Unregister a marker by providing the registered private data. */ -extern void *marker_probe_unregister_private_data (void *private); +extern int marker_probe_unregister_private_data (marker_probe_func *probe, +void *probe_private); -extern int marker_arm (const char *name); -extern int marker_disarm (const char *name); -extern void *marker_get_private_data (const char *name); +extern void *marker_get_private_data (const char *name,marker_probe_func *probe, +int num);#endif
kernel/marker.c +499 -172
911 lines not on this page · raw message
kernel/module.c +3 -4
@@ -1992,7 +1992,7 @@ static struct module *load_module(void _#ifdef CONFIG_MARKERS if (!mod->taints)marker_update_probe_range (mod->markers, - mod->markers + mod->num_markers,NULL ,NULL ); + mod->markers + mod->num_markers);#endif err =module_finalize (hdr, sechdrs, mod);if (err <0 )
@@ -2587,7 +2587,7 @@ EXPORT_SYMBOL(struct_module);#endif #ifdef CONFIG_MARKERS -void module_update_markers (struct module *probe_module,int *refcount) +void module_update_markers (void ) {struct module *mod;
@@ -2595,8 +2595,7 @@ void module_update_markers(struct modulelist_for_each_entry (mod, &modules, list)if (!mod->taints)marker_update_probe_range (mod->markers, - mod->markers + mod->num_markers, - probe_module, refcount); + mod->markers + mod->num_markers); mutex_unlock(&module_mutex); }#endif
include/linux/module.h +1 -1
@@ -462,7 +462,7 @@ int unregister_module_notifier(struct noextern void print_modules (void ); -extern void module_update_markers (struct module *probe_module,int *refcount); +extern void module_update_markers (void );#else /* !CONFIG_MODULES... */ #define EXPORT_SYMBOL (sym)
samples/markers/probe-example.c +9 -16
@@ -20,31 +20,27 @@ struct probe_data {marker_probe_func *probe_func; }; -void probe_subsystem_event (const struct marker *mdata,void *private, -const char *format, ...) +void probe_subsystem_event (void *probe_data,void *call_data, +const char *format,va_list *args) { -va_list ap;/* Declare args */ unsigned int value;const char *mystr;/* Assign args */ -va_start (ap, format); - value =va_arg (ap,typeof (value)); - mystr =va_arg (ap,typeof (mystr)); + value =va_arg (*args,typeof (value)); + mystr =va_arg (*args,typeof (mystr));/* Call printk */ -printk (KERN_DEBUG "Value %u, string %s\n" , value, mystr); +printk (KERN_INFO "Value %u, string %s\n" , value, mystr);/* or count, check rights, serialize data in a buffer */ - -va_end (ap); }atomic_t eventb_count =ATOMIC_INIT (0 ); -void probe_subsystem_eventb (const struct marker *mdata,void *private, -const char *format, ...) +void probe_subsystem_eventb (void *probe_data,void *call_data, +const char *format,va_list *args) {/* Increment counter */ atomic_inc (&eventb_count);
@@ -72,10 +68,6 @@ static int __init probe_init(void)if (result)printk (KERN_INFO "Unable to register probe %s\n" , probe_array[i].name); - result =marker_arm (probe_array[i].name); -if (result) -printk (KERN_INFO "Unable to arm probe %s\n" , - probe_array[i].name); }return 0 ; }
@@ -85,7 +77,8 @@ static void __exit probe_fini(void)int i;for (i =0 ; i <ARRAY_SIZE (probe_array); i++) -marker_probe_unregister (probe_array[i].name); +marker_probe_unregister (probe_array[i].name, + probe_array[i].probe_func, &probe_array[i]);printk (KERN_INFO "Number of event b : %u\n" ,atomic_read (&eventb_count)); }
Signature · 3 lines
Mathieu Desnoyers Computer Engineering Ph.D. Student, Ecole Polytechnique de Montreal OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
Quoted 1 line · Mathieu Desnoyers
RCU style multiple probes support for the Linux Kernel Markers.
Why would we want this?
Quoted 5 lines · * Christoph Hellwig ([email protected]) wrote:
Quoted 1 line · Mathieu Desnoyers
RCU style multiple probes support for the Linux Kernel Markers.
Why would we want this?
If we want to have more than one probe connected to a marker at a given time (LTTng, or blktrace, ssytemtap) then we need this patch. Without it, connecting a second probe handler to a marker will fail.
It allow us, for instance, to do interesting combinations :
Do standard tracing with LTTng and, eventually, to compute statistics with SystemTAP, or to have a special trigger on an event that would call a systemtap script which would stop flight recorder tracing.
Mathieu
Signature · 3 lines
Mathieu Desnoyers Computer Engineering Ph.D. Student, Ecole Polytechnique de Montreal OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68