Skip to content

Commit

Permalink
Merge branch 'bpf: Speed up symbol resolving in kprobe multi link'
Browse files Browse the repository at this point in the history
Jiri Olsa says:

====================

hi,
sending additional fix for symbol resolving in kprobe multi link
requested by Alexei and Andrii [1].

This speeds up bpftrace kprobe attachment, when using pure symbols
(3344 symbols) to attach:

Before:

  # perf stat -r 5 -e cycles ./src/bpftrace -e 'kprobe:x* {  } i:ms:1 { exit(); }'
  ...
  6.5681 +- 0.0225 seconds time elapsed  ( +-  0.34% )

After:

  # perf stat -r 5 -e cycles ./src/bpftrace -e 'kprobe:x* {  } i:ms:1 { exit(); }'
  ...
  0.5661 +- 0.0275 seconds time elapsed  ( +-  4.85% )

v6 changes:
  - rewrote patch 1 changelog and fixed the line length [Christoph]

v5 changes:
  - added acks [Masami]
  - workaround in selftest for RCU warning by filtering out several
    functions to attach

v4 changes:
  - fix compile issue [kernel test robot]
  - added acks [Andrii]

v3 changes:
  - renamed kallsyms_lookup_names to ftrace_lookup_symbols
    and moved it to ftrace.c [Masami]
  - added ack [Andrii]
  - couple small test fixes [Andrii]

v2 changes (first version [2]):
  - removed the 2 seconds check [Alexei]
  - moving/forcing symbols sorting out of kallsyms_lookup_names function [Alexei]
  - skipping one array allocation and copy_from_user [Andrii]
  - several small fixes [Masami,Andrii]
  - build fix [kernel test robot]

thanks,
jirka

[1] https://lore.kernel.org/bpf/CAEf4BzZtQaiUxQ-sm_hH2qKPRaqGHyOfEsW96DxtBHRaKLoL3Q@mail.gmail.com/
[2] https://lore.kernel.org/bpf/20220407125224.310255-1-jolsa@kernel.org/
====================

Signed-off-by: Alexei Starovoitov <ast@kernel.org>
  • Loading branch information
Alexei Starovoitov committed May 10, 2022
2 parents 9376d38 + 5b6c7e5 commit cb41154
Show file tree
Hide file tree
Showing 8 changed files with 308 additions and 69 deletions.
6 changes: 6 additions & 0 deletions include/linux/ftrace.h
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,8 @@ int unregister_ftrace_function(struct ftrace_ops *ops);
extern void ftrace_stub(unsigned long a0, unsigned long a1,
struct ftrace_ops *op, struct ftrace_regs *fregs);


int ftrace_lookup_symbols(const char **sorted_syms, size_t cnt, unsigned long *addrs);
#else /* !CONFIG_FUNCTION_TRACER */
/*
* (un)register_ftrace_function must be a macro since the ops parameter
Expand All @@ -313,6 +315,10 @@ extern void ftrace_stub(unsigned long a0, unsigned long a1,
static inline void ftrace_kill(void) { }
static inline void ftrace_free_init_mem(void) { }
static inline void ftrace_free_mem(struct module *mod, void *start, void *end) { }
static inline int ftrace_lookup_symbols(const char **sorted_syms, size_t cnt, unsigned long *addrs)
{
return -EOPNOTSUPP;
}
#endif /* CONFIG_FUNCTION_TRACER */

struct ftrace_func_entry {
Expand Down
7 changes: 6 additions & 1 deletion include/linux/kallsyms.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,11 @@ static inline void *dereference_symbol_descriptor(void *ptr)
return ptr;
}

#ifdef CONFIG_KALLSYMS
int kallsyms_on_each_symbol(int (*fn)(void *, const char *, struct module *,
unsigned long),
void *data);

#ifdef CONFIG_KALLSYMS
/* Lookup the address for a symbol. Returns 0 if not found. */
unsigned long kallsyms_lookup_name(const char *name);

Expand Down Expand Up @@ -163,6 +163,11 @@ static inline bool kallsyms_show_value(const struct cred *cred)
return false;
}

static inline int kallsyms_on_each_symbol(int (*fn)(void *, const char *, struct module *,
unsigned long), void *data)
{
return -EOPNOTSUPP;
}
#endif /*CONFIG_KALLSYMS*/

static inline void print_ip_sym(const char *loglvl, unsigned long ip)
Expand Down
3 changes: 1 addition & 2 deletions kernel/kallsyms.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include <linux/compiler.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/bsearch.h>

/*
* These will be re-linked against their real values
Expand Down Expand Up @@ -228,7 +229,6 @@ unsigned long kallsyms_lookup_name(const char *name)
return module_kallsyms_lookup_name(name);
}

#ifdef CONFIG_LIVEPATCH
/*
* Iterate over all symbols in vmlinux. For symbols from modules use
* module_kallsyms_on_each_symbol instead.
Expand All @@ -251,7 +251,6 @@ int kallsyms_on_each_symbol(int (*fn)(void *, const char *, struct module *,
}
return 0;
}
#endif /* CONFIG_LIVEPATCH */

static unsigned long get_symbol_pos(unsigned long addr,
unsigned long *symbolsize,
Expand Down
112 changes: 66 additions & 46 deletions kernel/trace/bpf_trace.c
Original file line number Diff line number Diff line change
Expand Up @@ -2229,6 +2229,59 @@ struct bpf_kprobe_multi_run_ctx {
unsigned long entry_ip;
};

struct user_syms {
const char **syms;
char *buf;
};

static int copy_user_syms(struct user_syms *us, unsigned long __user *usyms, u32 cnt)
{
unsigned long __user usymbol;
const char **syms = NULL;
char *buf = NULL, *p;
int err = -ENOMEM;
unsigned int i;

syms = kvmalloc(cnt * sizeof(*syms), GFP_KERNEL);
if (!syms)
goto error;

buf = kvmalloc(cnt * KSYM_NAME_LEN, GFP_KERNEL);
if (!buf)
goto error;

for (p = buf, i = 0; i < cnt; i++) {
if (__get_user(usymbol, usyms + i)) {
err = -EFAULT;
goto error;
}
err = strncpy_from_user(p, (const char __user *) usymbol, KSYM_NAME_LEN);
if (err == KSYM_NAME_LEN)
err = -E2BIG;
if (err < 0)
goto error;
syms[i] = p;
p += err + 1;
}

us->syms = syms;
us->buf = buf;
return 0;

error:
if (err) {
kvfree(syms);
kvfree(buf);
}
return err;
}

static void free_user_syms(struct user_syms *us)
{
kvfree(us->syms);
kvfree(us->buf);
}

static void bpf_kprobe_multi_link_release(struct bpf_link *link)
{
struct bpf_kprobe_multi_link *kmulti_link;
Expand Down Expand Up @@ -2349,53 +2402,12 @@ kprobe_multi_link_handler(struct fprobe *fp, unsigned long entry_ip,
kprobe_multi_link_prog_run(link, entry_ip, regs);
}

static int
kprobe_multi_resolve_syms(const void __user *usyms, u32 cnt,
unsigned long *addrs)
static int symbols_cmp(const void *a, const void *b)
{
unsigned long addr, size;
const char __user **syms;
int err = -ENOMEM;
unsigned int i;
char *func;

size = cnt * sizeof(*syms);
syms = kvzalloc(size, GFP_KERNEL);
if (!syms)
return -ENOMEM;
const char **str_a = (const char **) a;
const char **str_b = (const char **) b;

func = kmalloc(KSYM_NAME_LEN, GFP_KERNEL);
if (!func)
goto error;

if (copy_from_user(syms, usyms, size)) {
err = -EFAULT;
goto error;
}

for (i = 0; i < cnt; i++) {
err = strncpy_from_user(func, syms[i], KSYM_NAME_LEN);
if (err == KSYM_NAME_LEN)
err = -E2BIG;
if (err < 0)
goto error;
err = -EINVAL;
addr = kallsyms_lookup_name(func);
if (!addr)
goto error;
if (!kallsyms_lookup_size_offset(addr, &size, NULL))
goto error;
addr = ftrace_location_range(addr, addr + size - 1);
if (!addr)
goto error;
addrs[i] = addr;
}

err = 0;
error:
kvfree(syms);
kfree(func);
return err;
return strcmp(*str_a, *str_b);
}

int bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
Expand Down Expand Up @@ -2441,7 +2453,15 @@ int bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr
goto error;
}
} else {
err = kprobe_multi_resolve_syms(usyms, cnt, addrs);
struct user_syms us;

err = copy_user_syms(&us, usyms, cnt);
if (err)
goto error;

sort(us.syms, cnt, sizeof(*us.syms), symbols_cmp, NULL);
err = ftrace_lookup_symbols(us.syms, cnt, addrs);
free_user_syms(&us);
if (err)
goto error;
}
Expand Down
32 changes: 12 additions & 20 deletions kernel/trace/fprobe.c
Original file line number Diff line number Diff line change
Expand Up @@ -85,39 +85,31 @@ static void fprobe_exit_handler(struct rethook_node *rh, void *data,
}
NOKPROBE_SYMBOL(fprobe_exit_handler);

static int symbols_cmp(const void *a, const void *b)
{
const char **str_a = (const char **) a;
const char **str_b = (const char **) b;

return strcmp(*str_a, *str_b);
}

/* Convert ftrace location address from symbols */
static unsigned long *get_ftrace_locations(const char **syms, int num)
{
unsigned long addr, size;
unsigned long *addrs;
int i;

/* Convert symbols to symbol address */
addrs = kcalloc(num, sizeof(*addrs), GFP_KERNEL);
if (!addrs)
return ERR_PTR(-ENOMEM);

for (i = 0; i < num; i++) {
addr = kallsyms_lookup_name(syms[i]);
if (!addr) /* Maybe wrong symbol */
goto error;

/* Convert symbol address to ftrace location. */
if (!kallsyms_lookup_size_offset(addr, &size, NULL) || !size)
goto error;
/* ftrace_lookup_symbols expects sorted symbols */
sort(syms, num, sizeof(*syms), symbols_cmp, NULL);

addr = ftrace_location_range(addr, addr + size - 1);
if (!addr) /* No dynamic ftrace there. */
goto error;
if (!ftrace_lookup_symbols(syms, num, addrs))
return addrs;

addrs[i] = addr;
}

return addrs;

error:
kfree(addrs);

return ERR_PTR(-ENOENT);
}

Expand Down
62 changes: 62 additions & 0 deletions kernel/trace/ftrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -7964,3 +7964,65 @@ ftrace_enable_sysctl(struct ctl_table *table, int write,
mutex_unlock(&ftrace_lock);
return ret;
}

static int symbols_cmp(const void *a, const void *b)
{
const char **str_a = (const char **) a;
const char **str_b = (const char **) b;

return strcmp(*str_a, *str_b);
}

struct kallsyms_data {
unsigned long *addrs;
const char **syms;
size_t cnt;
size_t found;
};

static int kallsyms_callback(void *data, const char *name,
struct module *mod, unsigned long addr)
{
struct kallsyms_data *args = data;

if (!bsearch(&name, args->syms, args->cnt, sizeof(*args->syms), symbols_cmp))
return 0;

addr = ftrace_location(addr);
if (!addr)
return 0;

args->addrs[args->found++] = addr;
return args->found == args->cnt ? 1 : 0;
}

/**
* ftrace_lookup_symbols - Lookup addresses for array of symbols
*
* @sorted_syms: array of symbols pointers symbols to resolve,
* must be alphabetically sorted
* @cnt: number of symbols/addresses in @syms/@addrs arrays
* @addrs: array for storing resulting addresses
*
* This function looks up addresses for array of symbols provided in
* @syms array (must be alphabetically sorted) and stores them in
* @addrs array, which needs to be big enough to store at least @cnt
* addresses.
*
* This function returns 0 if all provided symbols are found,
* -ESRCH otherwise.
*/
int ftrace_lookup_symbols(const char **sorted_syms, size_t cnt, unsigned long *addrs)
{
struct kallsyms_data args;
int err;

args.addrs = addrs;
args.syms = sorted_syms;
args.cnt = cnt;
args.found = 0;
err = kallsyms_on_each_symbol(kallsyms_callback, &args);
if (err < 0)
return err;
return args.found == args.cnt ? 0 : -ESRCH;
}
Loading

0 comments on commit cb41154

Please sign in to comment.