Skip to content

Commit cb41154

Browse files
author
Alexei Starovoitov
committed
Merge branch 'bpf: Speed up symbol resolving in kprobe multi link'
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/[email protected]/ ==================== Signed-off-by: Alexei Starovoitov <[email protected]>
2 parents 9376d38 + 5b6c7e5 commit cb41154

File tree

8 files changed

+308
-69
lines changed

8 files changed

+308
-69
lines changed

include/linux/ftrace.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,8 @@ int unregister_ftrace_function(struct ftrace_ops *ops);
303303
extern void ftrace_stub(unsigned long a0, unsigned long a1,
304304
struct ftrace_ops *op, struct ftrace_regs *fregs);
305305

306+
307+
int ftrace_lookup_symbols(const char **sorted_syms, size_t cnt, unsigned long *addrs);
306308
#else /* !CONFIG_FUNCTION_TRACER */
307309
/*
308310
* (un)register_ftrace_function must be a macro since the ops parameter
@@ -313,6 +315,10 @@ extern void ftrace_stub(unsigned long a0, unsigned long a1,
313315
static inline void ftrace_kill(void) { }
314316
static inline void ftrace_free_init_mem(void) { }
315317
static inline void ftrace_free_mem(struct module *mod, void *start, void *end) { }
318+
static inline int ftrace_lookup_symbols(const char **sorted_syms, size_t cnt, unsigned long *addrs)
319+
{
320+
return -EOPNOTSUPP;
321+
}
316322
#endif /* CONFIG_FUNCTION_TRACER */
317323

318324
struct ftrace_func_entry {

include/linux/kallsyms.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,11 @@ static inline void *dereference_symbol_descriptor(void *ptr)
6565
return ptr;
6666
}
6767

68+
#ifdef CONFIG_KALLSYMS
6869
int kallsyms_on_each_symbol(int (*fn)(void *, const char *, struct module *,
6970
unsigned long),
7071
void *data);
7172

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

@@ -163,6 +163,11 @@ static inline bool kallsyms_show_value(const struct cred *cred)
163163
return false;
164164
}
165165

166+
static inline int kallsyms_on_each_symbol(int (*fn)(void *, const char *, struct module *,
167+
unsigned long), void *data)
168+
{
169+
return -EOPNOTSUPP;
170+
}
166171
#endif /*CONFIG_KALLSYMS*/
167172

168173
static inline void print_ip_sym(const char *loglvl, unsigned long ip)

kernel/kallsyms.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include <linux/compiler.h>
3030
#include <linux/module.h>
3131
#include <linux/kernel.h>
32+
#include <linux/bsearch.h>
3233

3334
/*
3435
* These will be re-linked against their real values
@@ -228,7 +229,6 @@ unsigned long kallsyms_lookup_name(const char *name)
228229
return module_kallsyms_lookup_name(name);
229230
}
230231

231-
#ifdef CONFIG_LIVEPATCH
232232
/*
233233
* Iterate over all symbols in vmlinux. For symbols from modules use
234234
* module_kallsyms_on_each_symbol instead.
@@ -251,7 +251,6 @@ int kallsyms_on_each_symbol(int (*fn)(void *, const char *, struct module *,
251251
}
252252
return 0;
253253
}
254-
#endif /* CONFIG_LIVEPATCH */
255254

256255
static unsigned long get_symbol_pos(unsigned long addr,
257256
unsigned long *symbolsize,

kernel/trace/bpf_trace.c

Lines changed: 66 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -2229,6 +2229,59 @@ struct bpf_kprobe_multi_run_ctx {
22292229
unsigned long entry_ip;
22302230
};
22312231

2232+
struct user_syms {
2233+
const char **syms;
2234+
char *buf;
2235+
};
2236+
2237+
static int copy_user_syms(struct user_syms *us, unsigned long __user *usyms, u32 cnt)
2238+
{
2239+
unsigned long __user usymbol;
2240+
const char **syms = NULL;
2241+
char *buf = NULL, *p;
2242+
int err = -ENOMEM;
2243+
unsigned int i;
2244+
2245+
syms = kvmalloc(cnt * sizeof(*syms), GFP_KERNEL);
2246+
if (!syms)
2247+
goto error;
2248+
2249+
buf = kvmalloc(cnt * KSYM_NAME_LEN, GFP_KERNEL);
2250+
if (!buf)
2251+
goto error;
2252+
2253+
for (p = buf, i = 0; i < cnt; i++) {
2254+
if (__get_user(usymbol, usyms + i)) {
2255+
err = -EFAULT;
2256+
goto error;
2257+
}
2258+
err = strncpy_from_user(p, (const char __user *) usymbol, KSYM_NAME_LEN);
2259+
if (err == KSYM_NAME_LEN)
2260+
err = -E2BIG;
2261+
if (err < 0)
2262+
goto error;
2263+
syms[i] = p;
2264+
p += err + 1;
2265+
}
2266+
2267+
us->syms = syms;
2268+
us->buf = buf;
2269+
return 0;
2270+
2271+
error:
2272+
if (err) {
2273+
kvfree(syms);
2274+
kvfree(buf);
2275+
}
2276+
return err;
2277+
}
2278+
2279+
static void free_user_syms(struct user_syms *us)
2280+
{
2281+
kvfree(us->syms);
2282+
kvfree(us->buf);
2283+
}
2284+
22322285
static void bpf_kprobe_multi_link_release(struct bpf_link *link)
22332286
{
22342287
struct bpf_kprobe_multi_link *kmulti_link;
@@ -2349,53 +2402,12 @@ kprobe_multi_link_handler(struct fprobe *fp, unsigned long entry_ip,
23492402
kprobe_multi_link_prog_run(link, entry_ip, regs);
23502403
}
23512404

2352-
static int
2353-
kprobe_multi_resolve_syms(const void __user *usyms, u32 cnt,
2354-
unsigned long *addrs)
2405+
static int symbols_cmp(const void *a, const void *b)
23552406
{
2356-
unsigned long addr, size;
2357-
const char __user **syms;
2358-
int err = -ENOMEM;
2359-
unsigned int i;
2360-
char *func;
2361-
2362-
size = cnt * sizeof(*syms);
2363-
syms = kvzalloc(size, GFP_KERNEL);
2364-
if (!syms)
2365-
return -ENOMEM;
2407+
const char **str_a = (const char **) a;
2408+
const char **str_b = (const char **) b;
23662409

2367-
func = kmalloc(KSYM_NAME_LEN, GFP_KERNEL);
2368-
if (!func)
2369-
goto error;
2370-
2371-
if (copy_from_user(syms, usyms, size)) {
2372-
err = -EFAULT;
2373-
goto error;
2374-
}
2375-
2376-
for (i = 0; i < cnt; i++) {
2377-
err = strncpy_from_user(func, syms[i], KSYM_NAME_LEN);
2378-
if (err == KSYM_NAME_LEN)
2379-
err = -E2BIG;
2380-
if (err < 0)
2381-
goto error;
2382-
err = -EINVAL;
2383-
addr = kallsyms_lookup_name(func);
2384-
if (!addr)
2385-
goto error;
2386-
if (!kallsyms_lookup_size_offset(addr, &size, NULL))
2387-
goto error;
2388-
addr = ftrace_location_range(addr, addr + size - 1);
2389-
if (!addr)
2390-
goto error;
2391-
addrs[i] = addr;
2392-
}
2393-
2394-
err = 0;
2395-
error:
2396-
kvfree(syms);
2397-
kfree(func);
2398-
return err;
2410+
return strcmp(*str_a, *str_b);
23992411
}
24002412

24012413
int bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
@@ -2441,7 +2453,15 @@ int bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr
24412453
goto error;
24422454
}
24432455
} else {
2444-
err = kprobe_multi_resolve_syms(usyms, cnt, addrs);
2456+
struct user_syms us;
2457+
2458+
err = copy_user_syms(&us, usyms, cnt);
2459+
if (err)
2460+
goto error;
2461+
2462+
sort(us.syms, cnt, sizeof(*us.syms), symbols_cmp, NULL);
2463+
err = ftrace_lookup_symbols(us.syms, cnt, addrs);
2464+
free_user_syms(&us);
24452465
if (err)
24462466
goto error;
24472467
}

kernel/trace/fprobe.c

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -85,39 +85,31 @@ static void fprobe_exit_handler(struct rethook_node *rh, void *data,
8585
}
8686
NOKPROBE_SYMBOL(fprobe_exit_handler);
8787

88+
static int symbols_cmp(const void *a, const void *b)
89+
{
90+
const char **str_a = (const char **) a;
91+
const char **str_b = (const char **) b;
92+
93+
return strcmp(*str_a, *str_b);
94+
}
95+
8896
/* Convert ftrace location address from symbols */
8997
static unsigned long *get_ftrace_locations(const char **syms, int num)
9098
{
91-
unsigned long addr, size;
9299
unsigned long *addrs;
93-
int i;
94100

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

100-
for (i = 0; i < num; i++) {
101-
addr = kallsyms_lookup_name(syms[i]);
102-
if (!addr) /* Maybe wrong symbol */
103-
goto error;
104-
105-
/* Convert symbol address to ftrace location. */
106-
if (!kallsyms_lookup_size_offset(addr, &size, NULL) || !size)
107-
goto error;
106+
/* ftrace_lookup_symbols expects sorted symbols */
107+
sort(syms, num, sizeof(*syms), symbols_cmp, NULL);
108108

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

113-
addrs[i] = addr;
114-
}
115-
116-
return addrs;
117-
118-
error:
119112
kfree(addrs);
120-
121113
return ERR_PTR(-ENOENT);
122114
}
123115

kernel/trace/ftrace.c

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7964,3 +7964,65 @@ ftrace_enable_sysctl(struct ctl_table *table, int write,
79647964
mutex_unlock(&ftrace_lock);
79657965
return ret;
79667966
}
7967+
7968+
static int symbols_cmp(const void *a, const void *b)
7969+
{
7970+
const char **str_a = (const char **) a;
7971+
const char **str_b = (const char **) b;
7972+
7973+
return strcmp(*str_a, *str_b);
7974+
}
7975+
7976+
struct kallsyms_data {
7977+
unsigned long *addrs;
7978+
const char **syms;
7979+
size_t cnt;
7980+
size_t found;
7981+
};
7982+
7983+
static int kallsyms_callback(void *data, const char *name,
7984+
struct module *mod, unsigned long addr)
7985+
{
7986+
struct kallsyms_data *args = data;
7987+
7988+
if (!bsearch(&name, args->syms, args->cnt, sizeof(*args->syms), symbols_cmp))
7989+
return 0;
7990+
7991+
addr = ftrace_location(addr);
7992+
if (!addr)
7993+
return 0;
7994+
7995+
args->addrs[args->found++] = addr;
7996+
return args->found == args->cnt ? 1 : 0;
7997+
}
7998+
7999+
/**
8000+
* ftrace_lookup_symbols - Lookup addresses for array of symbols
8001+
*
8002+
* @sorted_syms: array of symbols pointers symbols to resolve,
8003+
* must be alphabetically sorted
8004+
* @cnt: number of symbols/addresses in @syms/@addrs arrays
8005+
* @addrs: array for storing resulting addresses
8006+
*
8007+
* This function looks up addresses for array of symbols provided in
8008+
* @syms array (must be alphabetically sorted) and stores them in
8009+
* @addrs array, which needs to be big enough to store at least @cnt
8010+
* addresses.
8011+
*
8012+
* This function returns 0 if all provided symbols are found,
8013+
* -ESRCH otherwise.
8014+
*/
8015+
int ftrace_lookup_symbols(const char **sorted_syms, size_t cnt, unsigned long *addrs)
8016+
{
8017+
struct kallsyms_data args;
8018+
int err;
8019+
8020+
args.addrs = addrs;
8021+
args.syms = sorted_syms;
8022+
args.cnt = cnt;
8023+
args.found = 0;
8024+
err = kallsyms_on_each_symbol(kallsyms_callback, &args);
8025+
if (err < 0)
8026+
return err;
8027+
return args.found == args.cnt ? 0 : -ESRCH;
8028+
}

0 commit comments

Comments
 (0)