/* * Kprobes-based tracing events * * Created by Masami Hiramatsu <mhiramat@redhat.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * 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 <linux/module.h>#include <linux/uaccess.h> #include "trace_probe.h" #define KPROBE_EVENT_SYSTEM "kprobes" /** * Kprobe event core functions */struct trace_kprobe { struct list_head list; struct kretprobe rp; /* Use rp.kp for kprobe use */ unsigned long nhit; const char *symbol; /* symbol name */ struct trace_probe tp;}; struct event_file_link { struct ftrace_event_file *file; struct list_head list;}; #define SIZEOF_TRACE_KPROBE(n) \ (offsetof(struct trace_kprobe, tp.args) + \ (sizeof(struct probe_arg) * (n))) static __kprobes bool trace_kprobe_is_return(struct trace_kprobe *tk){ return tk->rp.handler != NULL;} static __kprobes const char *trace_kprobe_symbol(struct trace_kprobe *tk){ return tk->symbol ? tk->symbol : "unknown";} static __kprobes unsigned long trace_kprobe_offset(struct trace_kprobe *tk){ return tk->rp.kp.offset;} static __kprobes bool trace_kprobe_has_gone(struct trace_kprobe *tk){ return !!(kprobe_gone(&tk->rp.kp));} static __kprobes bool trace_kprobe_within_module(struct trace_kprobe *tk, struct module *mod){ int len = strlen(mod->name); const char *name = trace_kprobe_symbol(tk); return strncmp(mod->name, name, len) == 0 && name[len] == ':';} static __kprobes bool trace_kprobe_is_on_module(struct trace_kprobe *tk){ return !!strchr(trace_kprobe_symbol(tk), ':');} static int register_kprobe_event(struct trace_kprobe *tk);static int unregister_kprobe_event(struct trace_kprobe *tk); static DEFINE_MUTEX(probe_lock);static LIST_HEAD(probe_list); static int kprobe_dispatcher(struct kprobe *kp, struct pt_regs *regs);static int kretprobe_dispatcher(struct kretprobe_instance *ri, struct pt_regs *regs); /* Memory fetching by symbol */struct symbol_cache { char *symbol; long offset; unsigned long addr;}; unsigned long update_symbol_cache(struct symbol_cache *sc){ sc->addr = (unsigned long)kallsyms_lookup_name(sc->symbol); if (sc->addr) sc->addr += sc->offset; return sc->addr;} void free_symbol_cache(struct symbol_cache *sc){ kfree(sc->symbol); kfree(sc);} struct symbol_cache *alloc_symbol_cache(const char *sym, long offset){ struct symbol_cache *sc; if (!sym || strlen(sym) == 0) return NULL; sc = kzalloc(sizeof(struct symbol_cache), GFP_KERNEL); if (!sc) return NULL; sc->symbol = kstrdup(sym, GFP_KERNEL); if (!sc->symbol) { kfree(sc); return NULL; } sc->offset = offset; update_symbol_cache(sc); return sc;} /* * Kprobes-specific fetch functions */#define DEFINE_FETCH_stack(type) \static __kprobes void FETCH_FUNC_NAME(stack, type)(struct pt_regs *regs,\ void *offset, void *dest) \{ \ *(type *)dest = (type)regs_get_kernel_stack_nth(regs, \ (unsigned int)((unsigned long)offset)); \}DEFINE_BASIC_FETCH_FUNCS(stack)/* No string on the stack entry */#define fetch_stack_string NULL#define fetch_stack_string_size NULL #define DEFINE_FETCH_memory(type) \static __kprobes void FETCH_FUNC_NAME(memory, type)(struct pt_regs *regs,\ void *addr, void *dest) \{ \ type retval; \ if (probe_kernel_address(addr, retval)) \ *(type *)dest = 0; \ else \ *(type *)dest = retval; \}DEFINE_BASIC_FETCH_FUNCS(memory)/* * Fetch a null-terminated string. Caller MUST set *(u32 *)dest with max * length and relative data location. */static __kprobes void FETCH_FUNC_NAME(memory, string)(struct pt_regs *regs, void *addr, void *dest){ long ret; int maxlen = get_rloc_len(*(u32 *)dest); u8 *dst = get_rloc_data(dest); u8 *src = addr; mm_segment_t old_fs = get_fs(); if (!maxlen) return; /* * Try to get string again, since the string can be changed while * probing. */ set_fs(KERNEL_DS); pagefault_disable(); do ret = __copy_from_user_inatomic(dst++, src++, 1); while (dst[-1] && ret == 0 && src - (u8 *)addr < maxlen); dst[-1] = '\0'; pagefault_enable(); set_fs(old_fs); if (ret < 0) { /* Failed to fetch string */ ((u8 *)get_rloc_data(dest))[0] = '\0'; *(u32 *)dest = make_data_rloc(0, get_rloc_offs(*(u32 *)dest)); } else { *(u32 *)dest = make_data_rloc(src - (u8 *)addr, get_rloc_offs(*(u32 *)dest)); }} /* Return the length of string -- including null terminal byte */static __kprobes void FETCH_FUNC_NAME(memory, string_size)(struct pt_regs *regs, void *addr, void *dest){ mm_segment_t old_fs; int ret, len = 0; u8 c; old_fs = get_fs(); set_fs(KERNEL_DS); pagefault_disable(); do { ret = __copy_from_user_inatomic(&c, (u8 *)addr + len, 1); len++; } while (c && ret == 0 && len < MAX_STRING_SIZE); pagefault_enable(); set_fs(old_fs); if (ret < 0) /* Failed to check the length */ *(u32 *)dest = 0; else *(u32 *)dest = len;} #define DEFINE_FETCH_symbol(type) \__kprobes void FETCH_FUNC_NAME(symbol, type)(struct pt_regs *regs, \ void *data, void *dest) \{ \ struct symbol_cache *sc = data; \ if (sc->addr) \ fetch_memory_##type(regs, (void *)sc->addr, dest); \ else \ *(type *)dest = 0; \}DEFINE_BASIC_FETCH_FUNCS(symbol)DEFINE_FETCH_symbol(string)DEFINE_FETCH_symbol(string_size) /* kprobes don't support file_offset fetch methods */#define fetch_file_offset_u8 NULL#define fetch_file_offset_u16 NULL#define fetch_file_offset_u32 NULL#define fetch_file_offset_u64 NULL#define fetch_file_offset_string NULL#define fetch_file_offset_string_size NULL /* Fetch type information table */const struct fetch_type kprobes_fetch_type_table[] = { /* Special types */ [FETCH_TYPE_STRING] = __ASSIGN_FETCH_TYPE("string", string, string, sizeof(u32), 1, "__data_loc char[]"), [FETCH_TYPE_STRSIZE] = __ASSIGN_FETCH_TYPE("string_size", u32, string_size, sizeof(u32), 0, "u32"), /* Basic types */ ASSIGN_FETCH_TYPE(u8, u8, 0), ASSIGN_FETCH_TYPE(u16, u16, 0), ASSIGN_FETCH_TYPE(u32, u32, 0), ASSIGN_FETCH_TYPE(u64, u64, 0), ASSIGN_FETCH_TYPE(s8, u8, 1), ASSIGN_FETCH_TYPE(s16, u16, 1), ASSIGN_FETCH_TYPE(s32, u32, 1), ASSIGN_FETCH_TYPE(s64, u64, 1), ASSIGN_FETCH_TYPE_END}; /* * Allocate new trace_probe and initialize it (including kprobes). */static struct trace_kprobe *alloc_trace_kprobe(const char *group, const char *event, void *addr, const char *symbol, unsigned long offs, int nargs, bool is_return){ struct trace_kprobe *tk; int ret = -ENOMEM; tk = kzalloc(SIZEOF_TRACE_KPROBE(nargs), GFP_KERNEL); if (!tk) return ERR_PTR(ret); if (symbol) { tk->symbol = kstrdup(symbol, GFP_KERNEL); if (!tk->symbol) goto error; tk->rp.kp.symbol_name = tk->symbol; tk->rp.kp.offset = offs; } else tk->rp.kp.addr = addr; if (is_return) tk->rp.handler = kretprobe_dispatcher; else tk->rp.kp.pre_handler = kprobe_dispatcher; if (!event || !is_good_name(event)) { ret = -EINVAL; goto error; } tk->tp.call.class = &tk->tp.class; tk->tp.call.name = kstrdup(event, GFP_KERNEL); if (!tk->tp.call.name) goto error; if (!group || !is_good_name(group)) { ret = -EINVAL; goto error; } tk->tp.class.system = kstrdup(group, GFP_KERNEL); if (!tk->tp.class.system) goto error; INIT_LIST_HEAD(&tk->list); INIT_LIST_HEAD(&tk->tp.files); return tk;error: kfree(tk->tp.call.name); kfree(tk->symbol); kfree(tk); return ERR_PTR(ret);} static void free_trace_kprobe(struct trace_kprobe *tk){ int i; for (i = 0; i < tk->tp.nr_args; i++) traceprobe_free_probe_arg(&tk->tp.args[i]); kfree(tk->tp.call.class->system); kfree(tk->tp.call.name); kfree(tk->symbol); kfree(tk);} static struct trace_kprobe *find_trace_kprobe(const char *event, const char *group){ struct trace_kprobe *tk; list_for_each_entry(tk, &probe_list, list) if (strcmp(tk->tp.call.name, event) == 0 && strcmp(tk->tp.call.class->system, group) == 0) return tk; return NULL;} /* * Enable trace_probe * if the file is NULL, enable "perf" handler, or enable "trace" handler. */static intenable_trace_kprobe(struct trace_kprobe *tk, struct ftrace_event_file *file){ int ret = 0; if (file) { struct event_file_link *link; link = kmalloc(sizeof(*link), GFP_KERNEL); if (!link) { ret = -ENOMEM; goto out; } link->file = file; list_add_tail_rcu(&link->list, &tk->tp.files); tk->tp.flags |= TP_FLAG_TRACE; } else tk->tp.flags |= TP_FLAG_PROFILE; if (trace_probe_is_registered(&tk->tp) && !trace_kprobe_has_gone(tk)) { if (trace_kprobe_is_return(tk)) ret = enable_kretprobe(&tk->rp); else ret = enable_kprobe(&tk->rp.kp); } out: return ret;} static struct event_file_link *find_event_file_link(struct trace_probe *tp, struct ftrace_event_file *file){ struct event_file_link *link; list_for_each_entry(link, &tp->files, list) if (link->file == file) return link; return NULL;} /* * Disable trace_probe * if the file is NULL, disable "perf" handler, or disable "trace" handler. */static intdisable_trace_kprobe(struct trace_kprobe *tk, struct ftrace_event_file *file){ struct event_file_link *link = NULL; int wait = 0; int ret = 0; if (file) { link = find_event_file_link(&tk->tp, file); if (!link) { ret = -EINVAL; goto out; } list_del_rcu(&link->list); wait = 1; if (!list_empty(&tk->tp.files)) goto out; tk->tp.flags &= ~TP_FLAG_TRACE; } else tk->tp.flags &= ~TP_FLAG_PROFILE; if (!trace_probe_is_enabled(&tk->tp) && trace_probe_is_registered(&tk->tp)) { if (trace_kprobe_is_return(tk)) disable_kretprobe(&tk->rp); else disable_kprobe(&tk->rp.kp); wait = 1; } out: if (wait) { /* * Synchronize with kprobe_trace_func/kretprobe_trace_func * to ensure disabled (all running handlers are finished). * This is not only for kfree(), but also the caller, * trace_remove_event_call() supposes it for releasing * event_call related objects, which will be accessed in * the kprobe_trace_func/kretprobe_trace_func. */ synchronize_sched(); kfree(link); /* Ignored if link == NULL */ } return ret;} /* Internal register function - just handle k*probes and flags */static int __register_trace_kprobe(struct trace_kprobe *tk){ int i, ret; if (trace_probe_is_registered(&tk->tp)) return -EINVAL; for (i = 0; i < tk->tp.nr_args; i++) traceprobe_update_arg(&tk->tp.args[i]); /* Set/clear disabled flag according to tp->flag */ if (trace_probe_is_enabled(&tk->tp)) tk->rp.kp.flags &= ~KPROBE_FLAG_DISABLED; else tk->rp.kp.flags |= KPROBE_FLAG_DISABLED; if (trace_kprobe_is_return(tk)) ret = register_kretprobe(&tk->rp); else ret = register_kprobe(&tk->rp.kp); if (ret == 0) tk->tp.flags |= TP_FLAG_REGISTERED; else { pr_warning("Could not insert probe at %s+%lu: %d\n", trace_kprobe_symbol(tk), trace_kprobe_offset(tk), ret); if (ret == -ENOENT && trace_kprobe_is_on_module(tk)) { pr_warning("This probe might be able to register after" "target module is loaded. Continue.\n"); ret = 0; } else if (ret == -EILSEQ) { pr_warning("Probing address(0x%p) is not an " "instruction boundary.\n", tk->rp.kp.addr); ret = -EINVAL; } } return ret;} /* Internal unregister function - just handle k*probes and flags */static void __unregister_trace_kprobe(struct trace_kprobe *tk){ if (trace_probe_is_registered(&tk->tp)) { if (trace_kprobe_is_return(tk)) unregister_kretprobe(&tk->rp);Lines 1–500 of 1494. Load more
/* * Kprobes-based tracing events * * Created by Masami Hiramatsu <mhiramat@redhat.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * 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 <linux/module.h> #include <linux/uaccess.h> #include "trace_probe.h" #define KPROBE_EVENT_SYSTEM "kprobes" /** * Kprobe event core functions */ struct trace_kprobe { struct list_head list; struct kretprobe rp; /* Use rp.kp for kprobe use */ unsigned long nhit; const char *symbol; /* symbol name */ struct trace_probe tp; }; struct event_file_link { struct ftrace_event_file *file; struct list_head list; }; #define SIZEOF_TRACE_KPROBE(n) \ (offsetof(struct trace_kprobe, tp.args) + \ (sizeof(struct probe_arg) * (n))) static __kprobes bool trace_kprobe_is_return(struct trace_kprobe *tk) { return tk->rp.handler != NULL; } static __kprobes const char *trace_kprobe_symbol(struct trace_kprobe *tk) { return tk->symbol ? tk->symbol : "unknown"; } static __kprobes unsigned long trace_kprobe_offset(struct trace_kprobe *tk) { return tk->rp.kp.offset; } static __kprobes bool trace_kprobe_has_gone(struct trace_kprobe *tk) { return !!(kprobe_gone(&tk->rp.kp)); } static __kprobes bool trace_kprobe_within_module(struct trace_kprobe *tk, struct module *mod) { int len = strlen(mod->name); const char *name = trace_kprobe_symbol(tk); return strncmp(mod->name, name, len) == 0 && name[len] == ':'; } static __kprobes bool trace_kprobe_is_on_module(struct trace_kprobe *tk) { return !!strchr(trace_kprobe_symbol(tk), ':'); } static int register_kprobe_event(struct trace_kprobe *tk); static int unregister_kprobe_event(struct trace_kprobe *tk); static DEFINE_MUTEX(probe_lock); static LIST_HEAD(probe_list); static int kprobe_dispatcher(struct kprobe *kp, struct pt_regs *regs); static int kretprobe_dispatcher(struct kretprobe_instance *ri, struct pt_regs *regs); /* Memory fetching by symbol */ struct symbol_cache { char *symbol; long offset; unsigned long addr; }; unsigned long update_symbol_cache(struct symbol_cache *sc) { sc->addr = (unsigned long)kallsyms_lookup_name(sc->symbol); if (sc->addr) sc->addr += sc->offset; return sc->addr; } void free_symbol_cache(struct symbol_cache *sc) { kfree(sc->symbol); kfree(sc); } struct symbol_cache *alloc_symbol_cache(const char *sym, long offset) { struct symbol_cache *sc; if (!sym || strlen(sym) == 0) return NULL; sc = kzalloc(sizeof(struct symbol_cache), GFP_KERNEL); if (!sc) return NULL; sc->symbol = kstrdup(sym, GFP_KERNEL); if (!sc->symbol) { kfree(sc); return NULL; } sc->offset = offset; update_symbol_cache(sc); return sc; } /* * Kprobes-specific fetch functions */ #define DEFINE_FETCH_stack(type) \ static __kprobes void FETCH_FUNC_NAME(stack, type)(struct pt_regs *regs,\ void *offset, void *dest) \ { \ *(type *)dest = (type)regs_get_kernel_stack_nth(regs, \ (unsigned int)((unsigned long)offset)); \ } DEFINE_BASIC_FETCH_FUNCS(stack) /* No string on the stack entry */ #define fetch_stack_string NULL #define fetch_stack_string_size NULL #define DEFINE_FETCH_memory(type) \ static __kprobes void FETCH_FUNC_NAME(memory, type)(struct pt_regs *regs,\ void *addr, void *dest) \ { \ type retval; \ if (probe_kernel_address(addr, retval)) \ *(type *)dest = 0; \ else \ *(type *)dest = retval; \ } DEFINE_BASIC_FETCH_FUNCS(memory) /* * Fetch a null-terminated string. Caller MUST set *(u32 *)dest with max * length and relative data location. */ static __kprobes void FETCH_FUNC_NAME(memory, string)(struct pt_regs *regs, void *addr, void *dest) { long ret; int maxlen = get_rloc_len(*(u32 *)dest); u8 *dst = get_rloc_data(dest); u8 *src = addr; mm_segment_t old_fs = get_fs(); if (!maxlen) return; /* * Try to get string again, since the string can be changed while * probing. */ set_fs(KERNEL_DS); pagefault_disable(); do ret = __copy_from_user_inatomic(dst++, src++, 1); while (dst[-1] && ret == 0 && src - (u8 *)addr < maxlen); dst[-1] = '\0'; pagefault_enable(); set_fs(old_fs); if (ret < 0) { /* Failed to fetch string */ ((u8 *)get_rloc_data(dest))[0] = '\0'; *(u32 *)dest = make_data_rloc(0, get_rloc_offs(*(u32 *)dest)); } else { *(u32 *)dest = make_data_rloc(src - (u8 *)addr, get_rloc_offs(*(u32 *)dest)); } } /* Return the length of string -- including null terminal byte */ static __kprobes void FETCH_FUNC_NAME(memory, string_size)(struct pt_regs *regs, void *addr, void *dest) { mm_segment_t old_fs; int ret, len = 0; u8 c; old_fs = get_fs(); set_fs(KERNEL_DS); pagefault_disable(); do { ret = __copy_from_user_inatomic(&c, (u8 *)addr + len, 1); len++; } while (c && ret == 0 && len < MAX_STRING_SIZE); pagefault_enable(); set_fs(old_fs); if (ret < 0) /* Failed to check the length */ *(u32 *)dest = 0; else *(u32 *)dest = len; } #define DEFINE_FETCH_symbol(type) \ __kprobes void FETCH_FUNC_NAME(symbol, type)(struct pt_regs *regs, \ void *data, void *dest) \ { \ struct symbol_cache *sc = data; \ if (sc->addr) \ fetch_memory_##type(regs, (void *)sc->addr, dest); \ else \ *(type *)dest = 0; \ } DEFINE_BASIC_FETCH_FUNCS(symbol) DEFINE_FETCH_symbol(string) DEFINE_FETCH_symbol(string_size) /* kprobes don't support file_offset fetch methods */ #define fetch_file_offset_u8 NULL #define fetch_file_offset_u16 NULL #define fetch_file_offset_u32 NULL #define fetch_file_offset_u64 NULL #define fetch_file_offset_string NULL #define fetch_file_offset_string_size NULL /* Fetch type information table */ const struct fetch_type kprobes_fetch_type_table[] = { /* Special types */ [FETCH_TYPE_STRING] = __ASSIGN_FETCH_TYPE("string", string, string, sizeof(u32), 1, "__data_loc char[]"), [FETCH_TYPE_STRSIZE] = __ASSIGN_FETCH_TYPE("string_size", u32, string_size, sizeof(u32), 0, "u32"), /* Basic types */ ASSIGN_FETCH_TYPE(u8, u8, 0), ASSIGN_FETCH_TYPE(u16, u16, 0), ASSIGN_FETCH_TYPE(u32, u32, 0), ASSIGN_FETCH_TYPE(u64, u64, 0), ASSIGN_FETCH_TYPE(s8, u8, 1), ASSIGN_FETCH_TYPE(s16, u16, 1), ASSIGN_FETCH_TYPE(s32, u32, 1), ASSIGN_FETCH_TYPE(s64, u64, 1), ASSIGN_FETCH_TYPE_END }; /* * Allocate new trace_probe and initialize it (including kprobes). */ static struct trace_kprobe *alloc_trace_kprobe(const char *group, const char *event, void *addr, const char *symbol, unsigned long offs, int nargs, bool is_return) { struct trace_kprobe *tk; int ret = -ENOMEM; tk = kzalloc(SIZEOF_TRACE_KPROBE(nargs), GFP_KERNEL); if (!tk) return ERR_PTR(ret); if (symbol) { tk->symbol = kstrdup(symbol, GFP_KERNEL); if (!tk->symbol) goto error; tk->rp.kp.symbol_name = tk->symbol; tk->rp.kp.offset = offs; } else tk->rp.kp.addr = addr; if (is_return) tk->rp.handler = kretprobe_dispatcher; else tk->rp.kp.pre_handler = kprobe_dispatcher; if (!event || !is_good_name(event)) { ret = -EINVAL; goto error; } tk->tp.call.class = &tk->tp.class; tk->tp.call.name = kstrdup(event, GFP_KERNEL); if (!tk->tp.call.name) goto error; if (!group || !is_good_name(group)) { ret = -EINVAL; goto error; } tk->tp.class.system = kstrdup(group, GFP_KERNEL); if (!tk->tp.class.system) goto error; INIT_LIST_HEAD(&tk->list); INIT_LIST_HEAD(&tk->tp.files); return tk; error: kfree(tk->tp.call.name); kfree(tk->symbol); kfree(tk); return ERR_PTR(ret); } static void free_trace_kprobe(struct trace_kprobe *tk) { int i; for (i = 0; i < tk->tp.nr_args; i++) traceprobe_free_probe_arg(&tk->tp.args[i]); kfree(tk->tp.call.class->system); kfree(tk->tp.call.name); kfree(tk->symbol); kfree(tk); } static struct trace_kprobe *find_trace_kprobe(const char *event, const char *group) { struct trace_kprobe *tk; list_for_each_entry(tk, &probe_list, list) if (strcmp(tk->tp.call.name, event) == 0 && strcmp(tk->tp.call.class->system, group) == 0) return tk; return NULL; } /* * Enable trace_probe * if the file is NULL, enable "perf" handler, or enable "trace" handler. */ static int enable_trace_kprobe(struct trace_kprobe *tk, struct ftrace_event_file *file) { int ret = 0; if (file) { struct event_file_link *link; link = kmalloc(sizeof(*link), GFP_KERNEL); if (!link) { ret = -ENOMEM; goto out; } link->file = file; list_add_tail_rcu(&link->list, &tk->tp.files); tk->tp.flags |= TP_FLAG_TRACE; } else tk->tp.flags |= TP_FLAG_PROFILE; if (trace_probe_is_registered(&tk->tp) && !trace_kprobe_has_gone(tk)) { if (trace_kprobe_is_return(tk)) ret = enable_kretprobe(&tk->rp); else ret = enable_kprobe(&tk->rp.kp); } out: return ret; } static struct event_file_link * find_event_file_link(struct trace_probe *tp, struct ftrace_event_file *file) { struct event_file_link *link; list_for_each_entry(link, &tp->files, list) if (link->file == file) return link; return NULL; } /* * Disable trace_probe * if the file is NULL, disable "perf" handler, or disable "trace" handler. */ static int disable_trace_kprobe(struct trace_kprobe *tk, struct ftrace_event_file *file) { struct event_file_link *link = NULL; int wait = 0; int ret = 0; if (file) { link = find_event_file_link(&tk->tp, file); if (!link) { ret = -EINVAL; goto out; } list_del_rcu(&link->list); wait = 1; if (!list_empty(&tk->tp.files)) goto out; tk->tp.flags &= ~TP_FLAG_TRACE; } else tk->tp.flags &= ~TP_FLAG_PROFILE; if (!trace_probe_is_enabled(&tk->tp) && trace_probe_is_registered(&tk->tp)) { if (trace_kprobe_is_return(tk)) disable_kretprobe(&tk->rp); else disable_kprobe(&tk->rp.kp); wait = 1; } out: if (wait) { /* * Synchronize with kprobe_trace_func/kretprobe_trace_func * to ensure disabled (all running handlers are finished). * This is not only for kfree(), but also the caller, * trace_remove_event_call() supposes it for releasing * event_call related objects, which will be accessed in * the kprobe_trace_func/kretprobe_trace_func. */ synchronize_sched(); kfree(link); /* Ignored if link == NULL */ } return ret; } /* Internal register function - just handle k*probes and flags */ static int __register_trace_kprobe(struct trace_kprobe *tk) { int i, ret; if (trace_probe_is_registered(&tk->tp)) return -EINVAL; for (i = 0; i < tk->tp.nr_args; i++) traceprobe_update_arg(&tk->tp.args[i]); /* Set/clear disabled flag according to tp->flag */ if (trace_probe_is_enabled(&tk->tp)) tk->rp.kp.flags &= ~KPROBE_FLAG_DISABLED; else tk->rp.kp.flags |= KPROBE_FLAG_DISABLED; if (trace_kprobe_is_return(tk)) ret = register_kretprobe(&tk->rp); else ret = register_kprobe(&tk->rp.kp); if (ret == 0) tk->tp.flags |= TP_FLAG_REGISTERED; else { pr_warning("Could not insert probe at %s+%lu: %d\n", trace_kprobe_symbol(tk), trace_kprobe_offset(tk), ret); if (ret == -ENOENT && trace_kprobe_is_on_module(tk)) { pr_warning("This probe might be able to register after" "target module is loaded. Continue.\n"); ret = 0; } else if (ret == -EILSEQ) { pr_warning("Probing address(0x%p) is not an " "instruction boundary.\n", tk->rp.kp.addr); ret = -EINVAL; } } return ret; } /* Internal unregister function - just handle k*probes and flags */ static void __unregister_trace_kprobe(struct trace_kprobe *tk) { if (trace_probe_is_registered(&tk->tp)) { if (trace_kprobe_is_return(tk)) unregister_kretprobe(&tk->rp);