On Wed, Feb 21, 2018 at 11:57:36PM +0900, Masami Hiramatsu wrote: > Replace {k,u}probe event argument fetching framework > with switch-case based. Currently that is implemented > with structures, macros and chain of function-pointers, > which is more complicated than necessary and may get > a performance penalty by retpoline. > > This simplify that with an array of "fetch_insn" (opcode > and oprands), and make process_fetch_insn() just > interprets it. No function pointers are used. I think it'd be nice to split this commit to 3 parts: * convert to the fetch_insn * remove fetch methods (now unused) * unify fetch type table Thanks, Namhyung > > Signed-off-by: Masami Hiramatsu <mhiramat@xxxxxxxxxx> > --- > Changes in v2: > - Allow $comm:string. > --- > kernel/trace/trace_kprobe.c | 314 +++++++++++----------------- > kernel/trace/trace_probe.c | 441 ++++++++++++--------------------------- > kernel/trace/trace_probe.h | 232 ++++----------------- > kernel/trace/trace_probe_tmpl.h | 120 +++++++++++ > kernel/trace/trace_uprobe.c | 150 +++++++------ > 5 files changed, 517 insertions(+), 740 deletions(-) > create mode 100644 kernel/trace/trace_probe_tmpl.h > > diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c > index a96328dfc012..5dd2d470cc7e 100644 > --- a/kernel/trace/trace_kprobe.c > +++ b/kernel/trace/trace_kprobe.c > @@ -24,6 +24,7 @@ > #include <linux/error-injection.h> > > #include "trace_probe.h" > +#include "trace_probe_tmpl.h" > > #define KPROBE_EVENT_SYSTEM "kprobes" > #define KRETPROBE_MAXACTIVE_MAX 4096 > @@ -121,184 +122,6 @@ 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 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)); \ > -} \ > -NOKPROBE_SYMBOL(FETCH_FUNC_NAME(stack, type)); > - > -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 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; \ > -} \ > -NOKPROBE_SYMBOL(FETCH_FUNC_NAME(memory, type)); > - > -DEFINE_BASIC_FETCH_FUNCS(memory) > -/* > - * Fetch a null-terminated string. Caller MUST set *(u32 *)dest with max > - * length and relative data location. > - */ > -static void FETCH_FUNC_NAME(memory, string)(struct pt_regs *regs, > - void *addr, void *dest) > -{ > - int maxlen = get_rloc_len(*(u32 *)dest); > - u8 *dst = get_rloc_data(dest); > - long ret; > - > - if (!maxlen) > - return; > - > - /* > - * Try to get string again, since the string can be changed while > - * probing. > - */ > - ret = strncpy_from_unsafe(dst, addr, maxlen); > - > - if (ret < 0) { /* Failed to fetch string */ > - dst[0] = '\0'; > - *(u32 *)dest = make_data_rloc(0, get_rloc_offs(*(u32 *)dest)); > - } else { > - *(u32 *)dest = make_data_rloc(ret, get_rloc_offs(*(u32 *)dest)); > - } > -} > -NOKPROBE_SYMBOL(FETCH_FUNC_NAME(memory, string)); > - > -/* Return the length of string -- including null terminal byte */ > -static 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; > -} > -NOKPROBE_SYMBOL(FETCH_FUNC_NAME(memory, string_size)); > - > -#define DEFINE_FETCH_symbol(type) \ > -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; \ > -} \ > -NOKPROBE_SYMBOL(FETCH_FUNC_NAME(symbol, type)); > - > -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 */ > -static 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_ALIAS(x8, u8, u8, 0), > - ASSIGN_FETCH_TYPE_ALIAS(x16, u16, u16, 0), > - ASSIGN_FETCH_TYPE_ALIAS(x32, u32, u32, 0), > - ASSIGN_FETCH_TYPE_ALIAS(x64, u64, u64, 0), > - > - ASSIGN_FETCH_TYPE_END > -}; > - > /* > * Allocate new trace_probe and initialize it (including kprobes). > */ > @@ -490,14 +313,11 @@ disable_trace_kprobe(struct trace_kprobe *tk, struct trace_event_file *file) > /* Internal register function - just handle k*probes and flags */ > static int __register_trace_kprobe(struct trace_kprobe *tk) > { > - int i, ret; > + int 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; > @@ -830,8 +650,7 @@ static int create_trace_kprobe(int argc, char **argv) > > /* Parse fetch argument */ > ret = traceprobe_parse_probe_arg(arg, &tk->tp.size, parg, > - is_return, true, > - kprobes_fetch_type_table); > + is_return, true); > if (ret) { > pr_info("Parse error at argument[%d]. (%d)\n", i, ret); > goto error; > @@ -985,6 +804,133 @@ static const struct file_operations kprobe_profile_ops = { > .release = seq_release, > }; > > +/* Kprobe specific fetch functions */ > + > +/* Return the length of string -- including null terminal byte */ > +static nokprobe_inline void > +fetch_store_strlen(unsigned long 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; > +} > + > +/* > + * Fetch a null-terminated string. Caller MUST set *(u32 *)buf with max > + * length and relative data location. > + */ > +static nokprobe_inline void > +fetch_store_string(unsigned long addr, void *dest) > +{ > + int maxlen = get_rloc_len(*(u32 *)dest); > + u8 *dst = get_rloc_data(dest); > + long ret; > + > + if (!maxlen) > + return; > + > + /* > + * Try to get string again, since the string can be changed while > + * probing. > + */ > + ret = strncpy_from_unsafe(dst, (void *)addr, maxlen); > + > + if (ret < 0) { /* Failed to fetch string */ > + dst[0] = '\0'; > + *(u32 *)dest = make_data_rloc(0, get_rloc_offs(*(u32 *)dest)); > + } else { > + *(u32 *)dest = make_data_rloc(ret, get_rloc_offs(*(u32 *)dest)); > + } > +} > + > +/* Note that we don't verify it, since the code does not come from user space */ > +static int > +process_fetch_insn(struct fetch_insn *code, struct pt_regs *regs, void *dest, > + bool pre) > +{ > + unsigned long val; > + int ret; > + > + /* 1st stage: get value from context */ > + switch (code->op) { > + case FETCH_OP_REG: > + val = regs_get_register(regs, code->param); > + break; > + case FETCH_OP_STACK: > + val = regs_get_kernel_stack_nth(regs, code->param); > + break; > + case FETCH_OP_STACKP: > + val = kernel_stack_pointer(regs); > + break; > + case FETCH_OP_RETVAL: > + val = regs_return_value(regs); > + break; > + case FETCH_OP_IMM: > + val = code->immediate; > + break; > + case FETCH_OP_COMM: > + val = (unsigned long)current->comm; > + break; > + default: > + return -EILSEQ; > + } > + code++; > + > + /* 2nd stage: dereference memory if needed */ > + while (code->op == FETCH_OP_DEREF) { > + ret = probe_kernel_read(&val, (void *)val + code->offset, > + sizeof(val)); > + if (ret) > + return ret; > + code++; > + } > + > + /* 3rd stage: store value to buffer */ > + switch (code->op) { > + case FETCH_OP_ST_RAW: > + fetch_store_raw(val, code, dest); > + break; > + case FETCH_OP_ST_MEM: > + probe_kernel_read(dest, (void *)val + code->offset, code->size); > + break; > + case FETCH_OP_ST_STRING: > + if (pre) > + fetch_store_strlen(val + code->offset, dest); > + else > + fetch_store_string(val + code->offset, dest); > + break; > + default: > + return -EILSEQ; > + } > + code++; > + > + /* 4th stage: modify stored value if needed */ > + if (code->op == FETCH_OP_MOD_BF) { > + fetch_apply_bitfield(code, dest); > + code++; > + } > + > + return code->op == FETCH_OP_END ? 0 : -EILSEQ; > +} > +NOKPROBE_SYMBOL(process_fetch_insn) > + > /* Kprobe handler */ > static nokprobe_inline void > __kprobe_trace_func(struct trace_kprobe *tk, struct pt_regs *regs, > diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c > index 37bdd56b4988..cd87490b3492 100644 > --- a/kernel/trace/trace_probe.c > +++ b/kernel/trace/trace_probe.c > @@ -73,176 +73,29 @@ int PRINT_TYPE_FUNC_NAME(string)(struct trace_seq *s, void *data, void *ent) > > const char PRINT_TYPE_FMT_NAME(string)[] = "\\\"%s\\\""; > > -#define CHECK_FETCH_FUNCS(method, fn) \ > - (((FETCH_FUNC_NAME(method, u8) == fn) || \ > - (FETCH_FUNC_NAME(method, u16) == fn) || \ > - (FETCH_FUNC_NAME(method, u32) == fn) || \ > - (FETCH_FUNC_NAME(method, u64) == fn) || \ > - (FETCH_FUNC_NAME(method, string) == fn) || \ > - (FETCH_FUNC_NAME(method, string_size) == fn)) \ > - && (fn != NULL)) > - > -/* Data fetch function templates */ > -#define DEFINE_FETCH_reg(type) \ > -void FETCH_FUNC_NAME(reg, type)(struct pt_regs *regs, void *offset, void *dest) \ > -{ \ > - *(type *)dest = (type)regs_get_register(regs, \ > - (unsigned int)((unsigned long)offset)); \ > -} \ > -NOKPROBE_SYMBOL(FETCH_FUNC_NAME(reg, type)); > -DEFINE_BASIC_FETCH_FUNCS(reg) > -/* No string on the register */ > -#define fetch_reg_string NULL > -#define fetch_reg_string_size NULL > - > -#define DEFINE_FETCH_retval(type) \ > -void FETCH_FUNC_NAME(retval, type)(struct pt_regs *regs, \ > - void *dummy, void *dest) \ > -{ \ > - *(type *)dest = (type)regs_return_value(regs); \ > -} \ > -NOKPROBE_SYMBOL(FETCH_FUNC_NAME(retval, type)); > -DEFINE_BASIC_FETCH_FUNCS(retval) > -/* No string on the retval */ > -#define fetch_retval_string NULL > -#define fetch_retval_string_size NULL > - > -/* Dereference memory access function */ > -struct deref_fetch_param { > - struct fetch_param orig; > - long offset; > - fetch_func_t fetch; > - fetch_func_t fetch_size; > -}; > - > -#define DEFINE_FETCH_deref(type) \ > -void FETCH_FUNC_NAME(deref, type)(struct pt_regs *regs, \ > - void *data, void *dest) \ > -{ \ > - struct deref_fetch_param *dprm = data; \ > - unsigned long addr; \ > - call_fetch(&dprm->orig, regs, &addr); \ > - if (addr) { \ > - addr += dprm->offset; \ > - dprm->fetch(regs, (void *)addr, dest); \ > - } else \ > - *(type *)dest = 0; \ > -} \ > -NOKPROBE_SYMBOL(FETCH_FUNC_NAME(deref, type)); > -DEFINE_BASIC_FETCH_FUNCS(deref) > -DEFINE_FETCH_deref(string) > - > -void FETCH_FUNC_NAME(deref, string_size)(struct pt_regs *regs, > - void *data, void *dest) > -{ > - struct deref_fetch_param *dprm = data; > - unsigned long addr; > - > - call_fetch(&dprm->orig, regs, &addr); > - if (addr && dprm->fetch_size) { > - addr += dprm->offset; > - dprm->fetch_size(regs, (void *)addr, dest); > - } else > - *(string_size *)dest = 0; > -} > -NOKPROBE_SYMBOL(FETCH_FUNC_NAME(deref, string_size)); > - > -static void update_deref_fetch_param(struct deref_fetch_param *data) > -{ > - if (CHECK_FETCH_FUNCS(deref, data->orig.fn)) > - update_deref_fetch_param(data->orig.data); > - else if (CHECK_FETCH_FUNCS(symbol, data->orig.fn)) > - update_symbol_cache(data->orig.data); > -} > -NOKPROBE_SYMBOL(update_deref_fetch_param); > - > -static void free_deref_fetch_param(struct deref_fetch_param *data) > -{ > - if (CHECK_FETCH_FUNCS(deref, data->orig.fn)) > - free_deref_fetch_param(data->orig.data); > - else if (CHECK_FETCH_FUNCS(symbol, data->orig.fn)) > - free_symbol_cache(data->orig.data); > - kfree(data); > -} > -NOKPROBE_SYMBOL(free_deref_fetch_param); > - > -/* Bitfield fetch function */ > -struct bitfield_fetch_param { > - struct fetch_param orig; > - unsigned char hi_shift; > - unsigned char low_shift; > +/* Fetch type information table */ > +static const struct fetch_type probe_fetch_types[] = { > + /* Special types */ > + __ASSIGN_FETCH_TYPE("string", string, string, sizeof(u32), 1, > + "__data_loc char[]"), > + /* 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_ALIAS(x8, u8, u8, 0), > + ASSIGN_FETCH_TYPE_ALIAS(x16, u16, u16, 0), > + ASSIGN_FETCH_TYPE_ALIAS(x32, u32, u32, 0), > + ASSIGN_FETCH_TYPE_ALIAS(x64, u64, u64, 0), > + > + ASSIGN_FETCH_TYPE_END > }; > > -#define DEFINE_FETCH_bitfield(type) \ > -void FETCH_FUNC_NAME(bitfield, type)(struct pt_regs *regs, \ > - void *data, void *dest) \ > -{ \ > - struct bitfield_fetch_param *bprm = data; \ > - type buf = 0; \ > - call_fetch(&bprm->orig, regs, &buf); \ > - if (buf) { \ > - buf <<= bprm->hi_shift; \ > - buf >>= bprm->low_shift; \ > - } \ > - *(type *)dest = buf; \ > -} \ > -NOKPROBE_SYMBOL(FETCH_FUNC_NAME(bitfield, type)); > -DEFINE_BASIC_FETCH_FUNCS(bitfield) > -#define fetch_bitfield_string NULL > -#define fetch_bitfield_string_size NULL > - > -static void > -update_bitfield_fetch_param(struct bitfield_fetch_param *data) > -{ > - /* > - * Don't check the bitfield itself, because this must be the > - * last fetch function. > - */ > - if (CHECK_FETCH_FUNCS(deref, data->orig.fn)) > - update_deref_fetch_param(data->orig.data); > - else if (CHECK_FETCH_FUNCS(symbol, data->orig.fn)) > - update_symbol_cache(data->orig.data); > -} > - > -static void > -free_bitfield_fetch_param(struct bitfield_fetch_param *data) > -{ > - /* > - * Don't check the bitfield itself, because this must be the > - * last fetch function. > - */ > - if (CHECK_FETCH_FUNCS(deref, data->orig.fn)) > - free_deref_fetch_param(data->orig.data); > - else if (CHECK_FETCH_FUNCS(symbol, data->orig.fn)) > - free_symbol_cache(data->orig.data); > - > - kfree(data); > -} > - > -void FETCH_FUNC_NAME(comm, string)(struct pt_regs *regs, > - void *data, void *dest) > -{ > - int maxlen = get_rloc_len(*(u32 *)dest); > - u8 *dst = get_rloc_data(dest); > - long ret; > - > - if (!maxlen) > - return; > - > - ret = strlcpy(dst, current->comm, maxlen); > - *(u32 *)dest = make_data_rloc(ret, get_rloc_offs(*(u32 *)dest)); > -} > -NOKPROBE_SYMBOL(FETCH_FUNC_NAME(comm, string)); > - > -void FETCH_FUNC_NAME(comm, string_size)(struct pt_regs *regs, > - void *data, void *dest) > -{ > - *(u32 *)dest = strlen(current->comm) + 1; > -} > -NOKPROBE_SYMBOL(FETCH_FUNC_NAME(comm, string_size)); > - > -static const struct fetch_type *find_fetch_type(const char *type, > - const struct fetch_type *ftbl) > +static const struct fetch_type *find_fetch_type(const char *type) > { > int i; > > @@ -263,58 +116,27 @@ static const struct fetch_type *find_fetch_type(const char *type, > > switch (bs) { > case 8: > - return find_fetch_type("u8", ftbl); > + return find_fetch_type("u8"); > case 16: > - return find_fetch_type("u16", ftbl); > + return find_fetch_type("u16"); > case 32: > - return find_fetch_type("u32", ftbl); > + return find_fetch_type("u32"); > case 64: > - return find_fetch_type("u64", ftbl); > + return find_fetch_type("u64"); > default: > goto fail; > } > } > > - for (i = 0; ftbl[i].name; i++) { > - if (strcmp(type, ftbl[i].name) == 0) > - return &ftbl[i]; > + for (i = 0; probe_fetch_types[i].name; i++) { > + if (strcmp(type, probe_fetch_types[i].name) == 0) > + return &probe_fetch_types[i]; > } > > fail: > return NULL; > } > > -/* Special function : only accept unsigned long */ > -static void fetch_kernel_stack_address(struct pt_regs *regs, void *dummy, void *dest) > -{ > - *(unsigned long *)dest = kernel_stack_pointer(regs); > -} > -NOKPROBE_SYMBOL(fetch_kernel_stack_address); > - > -static void fetch_user_stack_address(struct pt_regs *regs, void *dummy, void *dest) > -{ > - *(unsigned long *)dest = user_stack_pointer(regs); > -} > -NOKPROBE_SYMBOL(fetch_user_stack_address); > - > -static fetch_func_t get_fetch_size_function(const struct fetch_type *type, > - fetch_func_t orig_fn, > - const struct fetch_type *ftbl) > -{ > - int i; > - > - if (type != &ftbl[FETCH_TYPE_STRING]) > - return NULL; /* Only string type needs size function */ > - > - for (i = 0; i < FETCH_MTD_END; i++) > - if (type->fetch[i] == orig_fn) > - return ftbl[FETCH_TYPE_STRSIZE].fetch[i]; > - > - WARN_ON(1); /* This should not happen */ > - > - return NULL; > -} > - > /* Split symbol and offset. */ > int traceprobe_split_symbol_offset(char *symbol, long *offset) > { > @@ -341,7 +163,7 @@ int traceprobe_split_symbol_offset(char *symbol, long *offset) > #define PARAM_MAX_STACK (THREAD_SIZE / sizeof(unsigned long)) > > static int parse_probe_vars(char *arg, const struct fetch_type *t, > - struct fetch_param *f, bool is_return, > + struct fetch_insn *code, bool is_return, > bool is_kprobe) > { > int ret = 0; > @@ -349,33 +171,24 @@ static int parse_probe_vars(char *arg, const struct fetch_type *t, > > if (strcmp(arg, "retval") == 0) { > if (is_return) > - f->fn = t->fetch[FETCH_MTD_retval]; > + code->op = FETCH_OP_RETVAL; > else > ret = -EINVAL; > } else if (strncmp(arg, "stack", 5) == 0) { > if (arg[5] == '\0') { > - if (strcmp(t->name, DEFAULT_FETCH_TYPE_STR)) > - return -EINVAL; > - > - if (is_kprobe) > - f->fn = fetch_kernel_stack_address; > - else > - f->fn = fetch_user_stack_address; > + code->op = FETCH_OP_STACKP; > } else if (isdigit(arg[5])) { > ret = kstrtoul(arg + 5, 10, ¶m); > if (ret || (is_kprobe && param > PARAM_MAX_STACK)) > ret = -EINVAL; > else { > - f->fn = t->fetch[FETCH_MTD_stack]; > - f->data = (void *)param; > + code->op = FETCH_OP_STACK; > + code->param = (unsigned int)param; > } > } else > ret = -EINVAL; > } else if (strcmp(arg, "comm") == 0) { > - if (strcmp(t->name, "string") != 0 && > - strcmp(t->name, "string_size") != 0) > - return -EINVAL; > - f->fn = t->fetch[FETCH_MTD_comm]; > + code->op = FETCH_OP_COMM; > } else > ret = -EINVAL; > > @@ -383,10 +196,12 @@ static int parse_probe_vars(char *arg, const struct fetch_type *t, > } > > /* Recursive argument parser */ > -static int parse_probe_arg(char *arg, const struct fetch_type *t, > - struct fetch_param *f, bool is_return, bool is_kprobe, > - const struct fetch_type *ftbl) > +static int > +parse_probe_arg(char *arg, const struct fetch_type *type, > + struct fetch_insn **pcode, struct fetch_insn *end, > + bool is_return, bool is_kprobe) > { > + struct fetch_insn *code = *pcode; > unsigned long param; > long offset; > char *tmp; > @@ -394,14 +209,15 @@ static int parse_probe_arg(char *arg, const struct fetch_type *t, > > switch (arg[0]) { > case '$': > - ret = parse_probe_vars(arg + 1, t, f, is_return, is_kprobe); > + ret = parse_probe_vars(arg + 1, type, code, > + is_return, is_kprobe); > break; > > case '%': /* named register */ > ret = regs_query_register_offset(arg + 1); > if (ret >= 0) { > - f->fn = t->fetch[FETCH_MTD_reg]; > - f->data = (void *)(unsigned long)ret; > + code->op = FETCH_OP_REG; > + code->param = (unsigned int)ret; > ret = 0; > } > break; > @@ -411,9 +227,9 @@ static int parse_probe_arg(char *arg, const struct fetch_type *t, > ret = kstrtoul(arg + 1, 0, ¶m); > if (ret) > break; > - > - f->fn = t->fetch[FETCH_MTD_memory]; > - f->data = (void *)param; > + /* load address */ > + code->op = FETCH_OP_IMM; > + code->immediate = param; > } else if (arg[1] == '+') { > /* kprobes don't support file offsets */ > if (is_kprobe) > @@ -423,8 +239,8 @@ static int parse_probe_arg(char *arg, const struct fetch_type *t, > if (ret) > break; > > - f->fn = t->fetch[FETCH_MTD_file_offset]; > - f->data = (void *)offset; > + code->op = FETCH_OP_FOFFS; > + code->immediate = (unsigned long)offset; // imm64? > } else { > /* uprobes don't support symbols */ > if (!is_kprobe) > @@ -434,10 +250,19 @@ static int parse_probe_arg(char *arg, const struct fetch_type *t, > if (ret) > break; > > - f->data = alloc_symbol_cache(arg + 1, offset); > - if (f->data) > - f->fn = t->fetch[FETCH_MTD_symbol]; > + code->op = FETCH_OP_IMM; > + code->immediate = > + (unsigned long)kallsyms_lookup_name(arg + 1); > + if (!code->immediate) > + return -ENOENT; > + code->immediate += offset; > } > + /* These are fetching from memory */ > + if (++code == end) > + return -E2BIG; > + *pcode = code; > + code->op = FETCH_OP_DEREF; > + code->offset = offset; > break; > > case '+': /* deref memory */ > @@ -445,11 +270,10 @@ static int parse_probe_arg(char *arg, const struct fetch_type *t, > case '-': > tmp = strchr(arg, '('); > if (!tmp) > - break; > + return -EINVAL; > > *tmp = '\0'; > ret = kstrtol(arg, 0, &offset); > - > if (ret) > break; > > @@ -457,36 +281,28 @@ static int parse_probe_arg(char *arg, const struct fetch_type *t, > tmp = strrchr(arg, ')'); > > if (tmp) { > - struct deref_fetch_param *dprm; > - const struct fetch_type *t2; > + const struct fetch_type *t2 = find_fetch_type(NULL); > > - t2 = find_fetch_type(NULL, ftbl); > *tmp = '\0'; > - dprm = kzalloc(sizeof(struct deref_fetch_param), GFP_KERNEL); > - > - if (!dprm) > - return -ENOMEM; > - > - dprm->offset = offset; > - dprm->fetch = t->fetch[FETCH_MTD_memory]; > - dprm->fetch_size = get_fetch_size_function(t, > - dprm->fetch, ftbl); > - ret = parse_probe_arg(arg, t2, &dprm->orig, is_return, > - is_kprobe, ftbl); > + ret = parse_probe_arg(arg, t2, &code, end, is_return, > + is_kprobe); > if (ret) > - kfree(dprm); > - else { > - f->fn = t->fetch[FETCH_MTD_deref]; > - f->data = (void *)dprm; > - } > + break; > + if (code->op == FETCH_OP_COMM) > + return -EINVAL; > + if (++code == end) > + return -E2BIG; > + *pcode = code; > + > + code->op = FETCH_OP_DEREF; > + code->offset = offset; > } > break; > } > - if (!ret && !f->fn) { /* Parsed, but do not find fetch method */ > - pr_info("%s type has no corresponding fetch method.\n", t->name); > + if (!ret && code->op == FETCH_OP_NOP) { > + /* Parsed, but do not find fetch method */ > ret = -EINVAL; > } > - > return ret; > } > > @@ -495,22 +311,15 @@ static int parse_probe_arg(char *arg, const struct fetch_type *t, > /* Bitfield type needs to be parsed into a fetch function */ > static int __parse_bitfield_probe_arg(const char *bf, > const struct fetch_type *t, > - struct fetch_param *f) > + struct fetch_insn **pcode) > { > - struct bitfield_fetch_param *bprm; > + struct fetch_insn *code = *pcode; > unsigned long bw, bo; > char *tail; > > if (*bf != 'b') > return 0; > > - bprm = kzalloc(sizeof(*bprm), GFP_KERNEL); > - if (!bprm) > - return -ENOMEM; > - > - bprm->orig = *f; > - f->fn = t->fetch[FETCH_MTD_bitfield]; > - f->data = (void *)bprm; > bw = simple_strtoul(bf + 1, &tail, 0); /* Use simple one */ > > if (bw == 0 || *tail != '@') > @@ -521,18 +330,24 @@ static int __parse_bitfield_probe_arg(const char *bf, > > if (tail == bf || *tail != '/') > return -EINVAL; > + code++; > + if (code->op != FETCH_OP_NOP) > + return -E2BIG; > + *pcode = code; > > - bprm->hi_shift = BYTES_TO_BITS(t->size) - (bw + bo); > - bprm->low_shift = bprm->hi_shift + bo; > + code->op = FETCH_OP_MOD_BF; > + code->lshift = BYTES_TO_BITS(t->size) - (bw + bo); > + code->rshift = BYTES_TO_BITS(t->size) - bw; > + code->basesize = t->size; > > return (BYTES_TO_BITS(t->size) < (bw + bo)) ? -EINVAL : 0; > } > > /* String length checking wrapper */ > int traceprobe_parse_probe_arg(char *arg, ssize_t *size, > - struct probe_arg *parg, bool is_return, bool is_kprobe, > - const struct fetch_type *ftbl) > + struct probe_arg *parg, bool is_return, bool is_kprobe) > { > + struct fetch_insn *code, *tmp = NULL; > const char *t; > int ret; > > @@ -556,25 +371,67 @@ int traceprobe_parse_probe_arg(char *arg, ssize_t *size, > */ > if (!t && strcmp(arg, "$comm") == 0) > t = "string"; > - parg->type = find_fetch_type(t, ftbl); > + parg->type = find_fetch_type(t); > if (!parg->type) { > pr_info("Unsupported type: %s\n", t); > return -EINVAL; > } > parg->offset = *size; > *size += parg->type->size; > - ret = parse_probe_arg(arg, parg->type, &parg->fetch, is_return, > - is_kprobe, ftbl); > - > - if (ret >= 0 && t != NULL) > - ret = __parse_bitfield_probe_arg(t, parg->type, &parg->fetch); > > - if (ret >= 0) { > - parg->fetch_size.fn = get_fetch_size_function(parg->type, > - parg->fetch.fn, > - ftbl); > - parg->fetch_size.data = parg->fetch.data; > + code = tmp = kzalloc(sizeof(*code) * FETCH_INSN_MAX, GFP_KERNEL); > + if (!code) > + return -ENOMEM; > + code[FETCH_INSN_MAX - 1].op = FETCH_OP_END; > + > + ret = parse_probe_arg(arg, parg->type, &code, &code[FETCH_INSN_MAX - 1], > + is_return, is_kprobe); > + if (ret) > + goto fail; > + > + /* Store operation */ > + if (!strcmp(parg->type->name, "string")) { > + if (code->op != FETCH_OP_DEREF && code->op != FETCH_OP_IMM && > + code->op != FETCH_OP_COMM) { > + pr_info("string only accepts memory or address.\n"); > + ret = -EINVAL; > + goto fail; > + } > + /* Since IMM or COMM must be the 1st insn, this is safe */ > + if (code->op == FETCH_OP_IMM || code->op == FETCH_OP_COMM) > + code++; > + code->op = FETCH_OP_ST_STRING; /* In DEREF case, replace it */ > + parg->dynamic = true; > + } else if (code->op == FETCH_OP_DEREF) { > + code->op = FETCH_OP_ST_MEM; > + code->size = parg->type->size; > + } else { > + code++; > + if (code->op != FETCH_OP_NOP) { > + ret = -E2BIG; > + goto fail; > + } > + code->op = FETCH_OP_ST_RAW; > + code->size = parg->type->size; > } > + /* Modify operation */ > + if (t != NULL) { > + ret = __parse_bitfield_probe_arg(t, parg->type, &code); > + if (ret) > + goto fail; > + } > + code++; > + code->op = FETCH_OP_END; > + > + /* Shrink down the code buffer */ > + parg->code = kzalloc(sizeof(*code) * (code - tmp + 1), GFP_KERNEL); > + if (!parg->code) > + ret = -ENOMEM; > + else > + memcpy(parg->code, tmp, sizeof(*code) * (code - tmp + 1)); > + > +fail: > + kfree(tmp); > > return ret; > } > @@ -596,25 +453,9 @@ int traceprobe_conflict_field_name(const char *name, > return 0; > } > > -void traceprobe_update_arg(struct probe_arg *arg) > -{ > - if (CHECK_FETCH_FUNCS(bitfield, arg->fetch.fn)) > - update_bitfield_fetch_param(arg->fetch.data); > - else if (CHECK_FETCH_FUNCS(deref, arg->fetch.fn)) > - update_deref_fetch_param(arg->fetch.data); > - else if (CHECK_FETCH_FUNCS(symbol, arg->fetch.fn)) > - update_symbol_cache(arg->fetch.data); > -} > - > void traceprobe_free_probe_arg(struct probe_arg *arg) > { > - if (CHECK_FETCH_FUNCS(bitfield, arg->fetch.fn)) > - free_bitfield_fetch_param(arg->fetch.data); > - else if (CHECK_FETCH_FUNCS(deref, arg->fetch.fn)) > - free_deref_fetch_param(arg->fetch.data); > - else if (CHECK_FETCH_FUNCS(symbol, arg->fetch.fn)) > - free_symbol_cache(arg->fetch.data); > - > + kfree(arg->code); > kfree(arg->name); > kfree(arg->comm); > } > diff --git a/kernel/trace/trace_probe.h b/kernel/trace/trace_probe.h > index de928052926b..89d853ef5174 100644 > --- a/kernel/trace/trace_probe.h > +++ b/kernel/trace/trace_probe.h > @@ -91,25 +91,50 @@ static nokprobe_inline void *get_loc_data(u32 *dl, void *ent) > return (u8 *)ent + get_rloc_offs(*dl); > } > > -/* Data fetch function type */ > -typedef void (*fetch_func_t)(struct pt_regs *, void *, void *); > /* Printing function type */ > typedef int (*print_type_func_t)(struct trace_seq *, void *, void *); > > -/* Fetch types */ > -enum { > - FETCH_MTD_reg = 0, > - FETCH_MTD_stack, > - FETCH_MTD_retval, > - FETCH_MTD_comm, > - FETCH_MTD_memory, > - FETCH_MTD_symbol, > - FETCH_MTD_deref, > - FETCH_MTD_bitfield, > - FETCH_MTD_file_offset, > - FETCH_MTD_END, > +enum fetch_op { > + FETCH_OP_NOP = 0, > + // Stage 1 (load) ops > + FETCH_OP_REG, /* Register : .param = offset */ > + FETCH_OP_STACK, /* Stack : .param = index */ > + FETCH_OP_STACKP, /* Stack pointer */ > + FETCH_OP_RETVAL, /* Return value */ > + FETCH_OP_IMM, /* Immediate : .immediate */ > + FETCH_OP_COMM, /* Current comm */ > + FETCH_OP_FOFFS, /* File offset: .immediate */ > + // Stage 2 (dereference) op > + FETCH_OP_DEREF, /* Dereference: .offset */ > + // Stage 3 (store) ops > + FETCH_OP_ST_RAW, /* Raw: .size */ > + FETCH_OP_ST_MEM, /* Mem: .offset, .size */ > + FETCH_OP_ST_STRING, /* String: .offset, .size */ > + // Stage 4 (modify) op > + FETCH_OP_MOD_BF, /* Bitfield: .basesize, .lshift, .rshift */ > + FETCH_OP_END, > }; > > +struct fetch_insn { > + enum fetch_op op; > + union { > + unsigned int param; > + struct { > + unsigned int size; > + int offset; > + }; > + struct { > + unsigned char basesize; > + unsigned char lshift; > + unsigned char rshift; > + }; > + unsigned long immediate; > + }; > +}; > + > +/* fetch + deref*N + store + mod + end <= 16, this allows N=12, enough */ > +#define FETCH_INSN_MAX 16 > + > /* Fetch type information table */ > struct fetch_type { > const char *name; /* Name of type */ > @@ -118,13 +143,6 @@ struct fetch_type { > print_type_func_t print; /* Print functions */ > const char *fmt; /* Fromat string */ > const char *fmttype; /* Name in format file */ > - /* Fetch functions */ > - fetch_func_t fetch[FETCH_MTD_END]; > -}; > - > -struct fetch_param { > - fetch_func_t fn; > - void *data; > }; > > /* For defining macros, define string/string_size types */ > @@ -154,66 +172,12 @@ DECLARE_BASIC_PRINT_TYPE_FUNC(x64); > > DECLARE_BASIC_PRINT_TYPE_FUNC(string); > > -#define FETCH_FUNC_NAME(method, type) fetch_##method##_##type > - > -/* Declare macro for basic types */ > -#define DECLARE_FETCH_FUNC(method, type) \ > -extern void FETCH_FUNC_NAME(method, type)(struct pt_regs *regs, \ > - void *data, void *dest) > - > -#define DECLARE_BASIC_FETCH_FUNCS(method) \ > -DECLARE_FETCH_FUNC(method, u8); \ > -DECLARE_FETCH_FUNC(method, u16); \ > -DECLARE_FETCH_FUNC(method, u32); \ > -DECLARE_FETCH_FUNC(method, u64) > - > -DECLARE_BASIC_FETCH_FUNCS(reg); > -#define fetch_reg_string NULL > -#define fetch_reg_string_size NULL > - > -DECLARE_BASIC_FETCH_FUNCS(retval); > -#define fetch_retval_string NULL > -#define fetch_retval_string_size NULL > - > -DECLARE_BASIC_FETCH_FUNCS(symbol); > -DECLARE_FETCH_FUNC(symbol, string); > -DECLARE_FETCH_FUNC(symbol, string_size); > - > -DECLARE_BASIC_FETCH_FUNCS(deref); > -DECLARE_FETCH_FUNC(deref, string); > -DECLARE_FETCH_FUNC(deref, string_size); > - > -DECLARE_BASIC_FETCH_FUNCS(bitfield); > -#define fetch_bitfield_string NULL > -#define fetch_bitfield_string_size NULL > - > -/* comm only makes sense as a string */ > -#define fetch_comm_u8 NULL > -#define fetch_comm_u16 NULL > -#define fetch_comm_u32 NULL > -#define fetch_comm_u64 NULL > -DECLARE_FETCH_FUNC(comm, string); > -DECLARE_FETCH_FUNC(comm, string_size); > - > -/* > - * Define macro for basic types - we don't need to define s* types, because > - * we have to care only about bitwidth at recording time. > - */ > -#define DEFINE_BASIC_FETCH_FUNCS(method) \ > -DEFINE_FETCH_##method(u8) \ > -DEFINE_FETCH_##method(u16) \ > -DEFINE_FETCH_##method(u32) \ > -DEFINE_FETCH_##method(u64) > - > /* Default (unsigned long) fetch type */ > #define __DEFAULT_FETCH_TYPE(t) x##t > #define _DEFAULT_FETCH_TYPE(t) __DEFAULT_FETCH_TYPE(t) > #define DEFAULT_FETCH_TYPE _DEFAULT_FETCH_TYPE(BITS_PER_LONG) > #define DEFAULT_FETCH_TYPE_STR __stringify(DEFAULT_FETCH_TYPE) > > -#define ASSIGN_FETCH_FUNC(method, type) \ > - [FETCH_MTD_##method] = FETCH_FUNC_NAME(method, type) > - > #define __ASSIGN_FETCH_TYPE(_name, ptype, ftype, _size, sign, _fmttype) \ > {.name = _name, \ > .size = _size, \ > @@ -221,17 +185,6 @@ DEFINE_FETCH_##method(u64) > .print = PRINT_TYPE_FUNC_NAME(ptype), \ > .fmt = PRINT_TYPE_FMT_NAME(ptype), \ > .fmttype = _fmttype, \ > - .fetch = { \ > -ASSIGN_FETCH_FUNC(reg, ftype), \ > -ASSIGN_FETCH_FUNC(stack, ftype), \ > -ASSIGN_FETCH_FUNC(retval, ftype), \ > -ASSIGN_FETCH_FUNC(comm, ftype), \ > -ASSIGN_FETCH_FUNC(memory, ftype), \ > -ASSIGN_FETCH_FUNC(symbol, ftype), \ > -ASSIGN_FETCH_FUNC(deref, ftype), \ > -ASSIGN_FETCH_FUNC(bitfield, ftype), \ > -ASSIGN_FETCH_FUNC(file_offset, ftype), \ > - } \ > } > > #define ASSIGN_FETCH_TYPE(ptype, ftype, sign) \ > @@ -243,42 +196,10 @@ ASSIGN_FETCH_FUNC(file_offset, ftype), \ > > #define ASSIGN_FETCH_TYPE_END {} > > -#define FETCH_TYPE_STRING 0 > -#define FETCH_TYPE_STRSIZE 1 > - > #ifdef CONFIG_KPROBE_EVENTS > -struct symbol_cache; > -unsigned long update_symbol_cache(struct symbol_cache *sc); > -void free_symbol_cache(struct symbol_cache *sc); > -struct symbol_cache *alloc_symbol_cache(const char *sym, long offset); > bool trace_kprobe_on_func_entry(struct trace_event_call *call); > bool trace_kprobe_error_injectable(struct trace_event_call *call); > #else > -/* uprobes do not support symbol fetch methods */ > -#define fetch_symbol_u8 NULL > -#define fetch_symbol_u16 NULL > -#define fetch_symbol_u32 NULL > -#define fetch_symbol_u64 NULL > -#define fetch_symbol_string NULL > -#define fetch_symbol_string_size NULL > - > -struct symbol_cache { > -}; > -static inline unsigned long __used update_symbol_cache(struct symbol_cache *sc) > -{ > - return 0; > -} > - > -static inline void __used free_symbol_cache(struct symbol_cache *sc) > -{ > -} > - > -static inline struct symbol_cache * __used > -alloc_symbol_cache(const char *sym, long offset) > -{ > - return NULL; > -} > - > static inline bool trace_kprobe_on_func_entry(struct trace_event_call *call) > { > return false; > @@ -291,8 +212,8 @@ static inline bool trace_kprobe_error_injectable(struct trace_event_call *call) > #endif /* CONFIG_KPROBE_EVENTS */ > > struct probe_arg { > - struct fetch_param fetch; > - struct fetch_param fetch_size; > + struct fetch_insn *code; > + bool dynamic;/* Dynamic array (string) is used */ > unsigned int offset; /* Offset from argument entry */ > const char *name; /* Name of this argument */ > const char *comm; /* Command of this argument */ > @@ -324,12 +245,6 @@ static inline bool trace_probe_is_registered(struct trace_probe *tp) > return !!(tp->flags & TP_FLAG_REGISTERED); > } > > -static nokprobe_inline void call_fetch(struct fetch_param *fprm, > - struct pt_regs *regs, void *dest) > -{ > - return fprm->fn(regs, fprm->data, dest); > -} > - > /* Check the name is good for event/group/fields */ > static inline bool is_good_name(const char *name) > { > @@ -355,8 +270,7 @@ find_event_file_link(struct trace_probe *tp, struct trace_event_file *file) > } > > extern int traceprobe_parse_probe_arg(char *arg, ssize_t *size, > - struct probe_arg *parg, bool is_return, bool is_kprobe, > - const struct fetch_type *ftbl); > + struct probe_arg *parg, bool is_return, bool is_kprobe); > > extern int traceprobe_conflict_field_name(const char *name, > struct probe_arg *args, int narg); > @@ -366,68 +280,6 @@ extern void traceprobe_free_probe_arg(struct probe_arg *arg); > > extern int traceprobe_split_symbol_offset(char *symbol, long *offset); > > -/* Sum up total data length for dynamic arraies (strings) */ > -static nokprobe_inline int > -__get_data_size(struct trace_probe *tp, struct pt_regs *regs) > -{ > - int i, ret = 0; > - u32 len; > - > - for (i = 0; i < tp->nr_args; i++) > - if (unlikely(tp->args[i].fetch_size.fn)) { > - call_fetch(&tp->args[i].fetch_size, regs, &len); > - ret += len; > - } > - > - return ret; > -} > - > -/* Store the value of each argument */ > -static nokprobe_inline void > -store_trace_args(int ent_size, struct trace_probe *tp, struct pt_regs *regs, > - u8 *data, int maxlen) > -{ > - int i; > - u32 end = tp->size; > - u32 *dl; /* Data (relative) location */ > - > - for (i = 0; i < tp->nr_args; i++) { > - if (unlikely(tp->args[i].fetch_size.fn)) { > - /* > - * First, we set the relative location and > - * maximum data length to *dl > - */ > - dl = (u32 *)(data + tp->args[i].offset); > - *dl = make_data_rloc(maxlen, end - tp->args[i].offset); > - /* Then try to fetch string or dynamic array data */ > - call_fetch(&tp->args[i].fetch, regs, dl); > - /* Reduce maximum length */ > - end += get_rloc_len(*dl); > - maxlen -= get_rloc_len(*dl); > - /* Trick here, convert data_rloc to data_loc */ > - *dl = convert_rloc_to_loc(*dl, > - ent_size + tp->args[i].offset); > - } else > - /* Just fetching data normally */ > - call_fetch(&tp->args[i].fetch, regs, > - data + tp->args[i].offset); > - } > -} > - > -static inline int > -print_probe_args(struct trace_seq *s, struct probe_arg *args, int nr_args, > - u8 *data, void *field) > -{ > - int i; > - > - for (i = 0; i < nr_args; i++) { > - trace_seq_printf(s, " %s=", args[i].name); > - if (!args[i].type->print(s, data + args[i].offset, field)) > - return -ENOMEM; > - } > - return 0; > -} > - > extern int set_print_fmt(struct trace_probe *tp, bool is_return); > > #ifdef CONFIG_PERF_EVENTS > diff --git a/kernel/trace/trace_probe_tmpl.h b/kernel/trace/trace_probe_tmpl.h > new file mode 100644 > index 000000000000..c8a5272abf01 > --- /dev/null > +++ b/kernel/trace/trace_probe_tmpl.h > @@ -0,0 +1,120 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Traceprobe fetch helper inlines > + */ > + > +static nokprobe_inline void > +fetch_store_raw(unsigned long val, struct fetch_insn *code, void *buf) > +{ > + switch (code->size) { > + case 1: > + *(u8 *)buf = (u8)val; > + break; > + case 2: > + *(u16 *)buf = (u16)val; > + break; > + case 4: > + *(u32 *)buf = (u32)val; > + break; > + case 8: > + //TBD: 32bit signed > + *(u64 *)buf = (u64)val; > + break; > + default: > + *(unsigned long *)buf = val; > + } > +} > + > +static nokprobe_inline void > +fetch_apply_bitfield(struct fetch_insn *code, void *buf) > +{ > + switch (code->basesize) { > + case 1: > + *(u8 *)buf <<= code->lshift; > + *(u8 *)buf >>= code->rshift; > + break; > + case 2: > + *(u16 *)buf <<= code->lshift; > + *(u16 *)buf >>= code->rshift; > + break; > + case 4: > + *(u32 *)buf <<= code->lshift; > + *(u32 *)buf >>= code->rshift; > + break; > + case 8: > + *(u64 *)buf <<= code->lshift; > + *(u64 *)buf >>= code->rshift; > + break; > + } > +} > + > +/* Define this for each callsite */ > +static int > +process_fetch_insn(struct fetch_insn *code, struct pt_regs *regs, > + void *dest, bool pre); > + > +/* Sum up total data length for dynamic arraies (strings) */ > +static nokprobe_inline int > +__get_data_size(struct trace_probe *tp, struct pt_regs *regs) > +{ > + struct probe_arg *arg; > + int i, ret = 0; > + u32 len; > + > + for (i = 0; i < tp->nr_args; i++) { > + arg = tp->args + i; > + if (unlikely(arg->dynamic)) { > + process_fetch_insn(arg->code, regs, &len, true); > + ret += len; > + } > + } > + > + return ret; > +} > + > +/* Store the value of each argument */ > +static nokprobe_inline void > +store_trace_args(int ent_size, struct trace_probe *tp, struct pt_regs *regs, > + u8 *data, int maxlen) > +{ > + struct probe_arg *arg; > + u32 end = tp->size; > + u32 *dl; /* Data (relative) location */ > + int i; > + > + for (i = 0; i < tp->nr_args; i++) { > + arg = tp->args + i; > + if (unlikely(arg->dynamic)) { > + /* > + * First, we set the relative location and > + * maximum data length to *dl > + */ > + dl = (u32 *)(data + arg->offset); > + *dl = make_data_rloc(maxlen, end - arg->offset); > + /* Then try to fetch string or dynamic array data */ > + process_fetch_insn(arg->code, regs, dl, false); > + /* Reduce maximum length */ > + end += get_rloc_len(*dl); > + maxlen -= get_rloc_len(*dl); > + /* Trick here, convert data_rloc to data_loc */ > + *dl = convert_rloc_to_loc(*dl, ent_size + arg->offset); > + } else > + /* Just fetching data normally */ > + process_fetch_insn(arg->code, regs, data + arg->offset, > + false); > + } > +} > + > +static inline int > +print_probe_args(struct trace_seq *s, struct probe_arg *args, int nr_args, > + u8 *data, void *field) > +{ > + int i; > + > + for (i = 0; i < nr_args; i++) { > + trace_seq_printf(s, " %s=", args[i].name); > + if (!args[i].type->print(s, data + args[i].offset, field)) > + return -ENOMEM; > + } > + return 0; > +} > diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c > index 887da2bb63aa..9fc0123c721f 100644 > --- a/kernel/trace/trace_uprobe.c > +++ b/kernel/trace/trace_uprobe.c > @@ -27,6 +27,7 @@ > #include <linux/rculist.h> > > #include "trace_probe.h" > +#include "trace_probe_tmpl.h" > > #define UPROBE_EVENT_SYSTEM "uprobes" > > @@ -109,37 +110,19 @@ static unsigned long get_user_stack_nth(struct pt_regs *regs, unsigned int n) > /* > * Uprobes-specific fetch functions > */ > -#define DEFINE_FETCH_stack(type) \ > -static void FETCH_FUNC_NAME(stack, type)(struct pt_regs *regs, \ > - void *offset, void *dest) \ > -{ \ > - *(type *)dest = (type)get_user_stack_nth(regs, \ > - ((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 void FETCH_FUNC_NAME(memory, type)(struct pt_regs *regs, \ > - void *addr, void *dest) \ > -{ \ > - type retval; \ > - void __user *vaddr = (void __force __user *) addr; \ > - \ > - if (copy_from_user(&retval, vaddr, sizeof(type))) \ > - *(type *)dest = 0; \ > - else \ > - *(type *) dest = retval; \ > +static nokprobe_inline int > +probe_user_read(void *dest, void *src, size_t size) > +{ > + void __user *vaddr = (void __force __user *)src; > + > + return copy_from_user(dest, vaddr, size); > } > -DEFINE_BASIC_FETCH_FUNCS(memory) > /* > * Fetch a null-terminated string. Caller MUST set *(u32 *)dest with max > * length and relative data location. > */ > -static void FETCH_FUNC_NAME(memory, string)(struct pt_regs *regs, > - void *addr, void *dest) > +static nokprobe_inline void > +fetch_store_string(unsigned long addr, void *dest) > { > long ret; > u32 rloc = *(u32 *)dest; > @@ -160,8 +143,9 @@ static void FETCH_FUNC_NAME(memory, string)(struct pt_regs *regs, > } > } > > -static void FETCH_FUNC_NAME(memory, string_size)(struct pt_regs *regs, > - void *addr, void *dest) > +/* Return the length of string -- including null terminal byte */ > +static nokprobe_inline void > +fetch_store_strlen(unsigned long addr, void *dest) > { > int len; > void __user *vaddr = (void __force __user *) addr; > @@ -174,7 +158,7 @@ static void FETCH_FUNC_NAME(memory, string_size)(struct pt_regs *regs, > *(u32 *)dest = len; > } > > -static unsigned long translate_user_vaddr(void *file_offset) > +static unsigned long translate_user_vaddr(unsigned long file_offset) > { > unsigned long base_addr; > struct uprobe_dispatch_data *udd; > @@ -182,44 +166,79 @@ static unsigned long translate_user_vaddr(void *file_offset) > udd = (void *) current->utask->vaddr; > > base_addr = udd->bp_addr - udd->tu->offset; > - return base_addr + (unsigned long)file_offset; > + return base_addr + file_offset; > } > > -#define DEFINE_FETCH_file_offset(type) \ > -static void FETCH_FUNC_NAME(file_offset, type)(struct pt_regs *regs, \ > - void *offset, void *dest)\ > -{ \ > - void *vaddr = (void *)translate_user_vaddr(offset); \ > - \ > - FETCH_FUNC_NAME(memory, type)(regs, vaddr, dest); \ > +/* Note that we don't verify it, since the code does not come from user space */ > +static int > +process_fetch_insn(struct fetch_insn *code, struct pt_regs *regs, void *dest, > + bool pre) > +{ > + unsigned long val; > + int ret; > + > + /* 1st stage: get value from context */ > + switch (code->op) { > + case FETCH_OP_REG: > + val = regs_get_register(regs, code->param); > + break; > + case FETCH_OP_STACK: > + val = get_user_stack_nth(regs, code->param); > + break; > + case FETCH_OP_STACKP: > + val = user_stack_pointer(regs); > + break; > + case FETCH_OP_RETVAL: > + val = regs_return_value(regs); > + break; > + case FETCH_OP_IMM: > + val = code->immediate; > + break; > + case FETCH_OP_FOFFS: > + val = translate_user_vaddr(code->immediate); > + break; > + default: > + return -EILSEQ; > + } > + code++; > + > + /* 2nd stage: dereference memory if needed */ > + while (code->op == FETCH_OP_DEREF) { > + ret = probe_user_read(&val, (void *)val + code->offset, > + sizeof(val)); > + if (ret) > + return ret; > + code++; > + } > + > + /* 3rd stage: store value to buffer */ > + switch (code->op) { > + case FETCH_OP_ST_RAW: > + fetch_store_raw(val, code, dest); > + break; > + case FETCH_OP_ST_MEM: > + probe_user_read(dest, (void *)val + code->offset, code->size); > + break; > + case FETCH_OP_ST_STRING: > + if (pre) > + fetch_store_strlen(val + code->offset, dest); > + else > + fetch_store_string(val + code->offset, dest); > + break; > + default: > + return -EILSEQ; > + } > + code++; > + > + /* 4th stage: modify stored value if needed */ > + if (code->op == FETCH_OP_MOD_BF) { > + fetch_apply_bitfield(code, dest); > + code++; > + } > + > + return code->op == FETCH_OP_END ? 0 : -EILSEQ; > } > -DEFINE_BASIC_FETCH_FUNCS(file_offset) > -DEFINE_FETCH_file_offset(string) > -DEFINE_FETCH_file_offset(string_size) > - > -/* Fetch type information table */ > -static const struct fetch_type uprobes_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_ALIAS(x8, u8, u8, 0), > - ASSIGN_FETCH_TYPE_ALIAS(x16, u16, u16, 0), > - ASSIGN_FETCH_TYPE_ALIAS(x32, u32, u32, 0), > - ASSIGN_FETCH_TYPE_ALIAS(x64, u64, u64, 0), > - > - ASSIGN_FETCH_TYPE_END > -}; > +NOKPROBE_SYMBOL(process_fetch_insn) > > static inline void init_trace_uprobe_filter(struct trace_uprobe_filter *filter) > { > @@ -538,8 +557,7 @@ static int create_trace_uprobe(int argc, char **argv) > > /* Parse fetch argument */ > ret = traceprobe_parse_probe_arg(arg, &tu->tp.size, parg, > - is_return, false, > - uprobes_fetch_type_table); > + is_return, false); > if (ret) { > pr_info("Parse error at argument[%d]. (%d)\n", i, ret); > goto error; > -- To unsubscribe from this list: send the line "unsubscribe linux-kselftest" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html