Message ID | 20200427201249.2995688-1-yhs@fb.com |
---|---|
State | Changes Requested |
Delegated to: | BPF Maintainers |
Headers | show |
Series | bpf: implement bpf iterator for kernel data | expand |
Hi Yonghong, I love your patch! Perhaps something to improve: [auto build test WARNING on bpf-next/master] [cannot apply to bpf/master net/master vhost/linux-next net-next/master linus/master v5.7-rc3 next-20200424] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system. BTW, we also suggest to use '--base' option to specify the base tree in git format-patch, please see https://stackoverflow.com/a/37406982] url: https://github.com/0day-ci/linux/commits/Yonghong-Song/bpf-implement-bpf-iterator-for-kernel-data/20200428-115101 base: https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git master config: sh-allmodconfig (attached as .config) compiler: sh4-linux-gcc (GCC) 9.3.0 reproduce: wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # save the attached .config to linux build tree COMPILER_INSTALL_PATH=$HOME/0day GCC_VERSION=9.3.0 make.cross ARCH=sh If you fix the issue, kindly add following tag as appropriate Reported-by: kbuild test robot <lkp@intel.com> All warnings (new ones prefixed by >>): In file included from kernel/trace/bpf_trace.c:10: kernel/trace/bpf_trace.c: In function 'bpf_seq_printf': >> kernel/trace/bpf_trace.c:463:35: warning: the frame size of 1672 bytes is larger than 1024 bytes [-Wframe-larger-than=] 463 | BPF_CALL_5(bpf_seq_printf, struct seq_file *, m, char *, fmt, u32, fmt_size, | ^~~~~~~~ include/linux/filter.h:456:30: note: in definition of macro '__BPF_CAST' 456 | (unsigned long)0, (t)0))) a | ^ >> include/linux/filter.h:449:27: note: in expansion of macro '__BPF_MAP_5' 449 | #define __BPF_MAP(n, ...) __BPF_MAP_##n(__VA_ARGS__) | ^~~~~~~~~~ >> include/linux/filter.h:474:35: note: in expansion of macro '__BPF_MAP' 474 | return ((btf_##name)____##name)(__BPF_MAP(x,__BPF_CAST,__BPF_N,__VA_ARGS__));\ | ^~~~~~~~~ >> include/linux/filter.h:484:31: note: in expansion of macro 'BPF_CALL_x' 484 | #define BPF_CALL_5(name, ...) BPF_CALL_x(5, name, __VA_ARGS__) | ^~~~~~~~~~ >> kernel/trace/bpf_trace.c:463:1: note: in expansion of macro 'BPF_CALL_5' 463 | BPF_CALL_5(bpf_seq_printf, struct seq_file *, m, char *, fmt, u32, fmt_size, | ^~~~~~~~~~ vim +463 kernel/trace/bpf_trace.c 462 > 463 BPF_CALL_5(bpf_seq_printf, struct seq_file *, m, char *, fmt, u32, fmt_size, 464 const void *, data, u32, data_len) 465 { 466 char bufs[MAX_SEQ_PRINTF_VARARGS][MAX_SEQ_PRINTF_STR_LEN]; 467 u64 params[MAX_SEQ_PRINTF_VARARGS]; 468 int i, copy_size, num_args; 469 const u64 *args = data; 470 int fmt_cnt = 0; 471 472 /* 473 * bpf_check()->check_func_arg()->check_stack_boundary() 474 * guarantees that fmt points to bpf program stack, 475 * fmt_size bytes of it were initialized and fmt_size > 0 476 */ 477 if (fmt[--fmt_size] != 0) 478 return -EINVAL; 479 480 if (data_len & 7) 481 return -EINVAL; 482 483 for (i = 0; i < fmt_size; i++) { 484 if (fmt[i] == '%' && (!data || !data_len)) 485 return -EINVAL; 486 } 487 488 num_args = data_len / 8; 489 490 /* check format string for allowed specifiers */ 491 for (i = 0; i < fmt_size; i++) { 492 if ((!isprint(fmt[i]) && !isspace(fmt[i])) || !isascii(fmt[i])) 493 return -EINVAL; 494 495 if (fmt[i] != '%') 496 continue; 497 498 if (fmt_cnt >= MAX_SEQ_PRINTF_VARARGS) 499 return -E2BIG; 500 501 if (fmt_cnt >= num_args) 502 return -EINVAL; 503 504 /* fmt[i] != 0 && fmt[last] == 0, so we can access fmt[i + 1] */ 505 i++; 506 507 /* skip optional "[0+-][num]" width formating field */ 508 while (fmt[i] == '0' || fmt[i] == '+' || fmt[i] == '-') 509 i++; 510 if (fmt[i] >= '1' && fmt[i] <= '9') { 511 i++; 512 while (fmt[i] >= '0' && fmt[i] <= '9') 513 i++; 514 } 515 516 if (fmt[i] == 's') { 517 /* disallow any further format extensions */ 518 if (fmt[i + 1] != 0 && 519 !isspace(fmt[i + 1]) && 520 !ispunct(fmt[i + 1])) 521 return -EINVAL; 522 523 /* try our best to copy */ 524 bufs[fmt_cnt][0] = 0; 525 strncpy_from_unsafe(bufs[fmt_cnt], 526 (void *) (long) args[fmt_cnt], 527 MAX_SEQ_PRINTF_STR_LEN); 528 params[fmt_cnt] = (u64)(long)bufs[fmt_cnt]; 529 530 fmt_cnt++; 531 continue; 532 } 533 534 if (fmt[i] == 'p') { 535 if (fmt[i + 1] == 0 || 536 fmt[i + 1] == 'K' || 537 fmt[i + 1] == 'x') { 538 /* just kernel pointers */ 539 params[fmt_cnt] = args[fmt_cnt]; 540 fmt_cnt++; 541 continue; 542 } 543 544 /* only support "%pI4", "%pi4", "%pI6" and "pi6". */ 545 if (fmt[i + 1] != 'i' && fmt[i + 1] != 'I') 546 return -EINVAL; 547 if (fmt[i + 2] != '4' && fmt[i + 2] != '6') 548 return -EINVAL; 549 550 copy_size = (fmt[i + 2] == '4') ? 4 : 16; 551 552 /* try our best to copy */ 553 probe_kernel_read(bufs[fmt_cnt], 554 (void *) (long) args[fmt_cnt], copy_size); 555 params[fmt_cnt] = (u64)(long)bufs[fmt_cnt]; 556 557 i += 2; 558 fmt_cnt++; 559 continue; 560 } 561 562 if (fmt[i] == 'l') { 563 i++; 564 if (fmt[i] == 'l') 565 i++; 566 } 567 568 if (fmt[i] != 'i' && fmt[i] != 'd' && 569 fmt[i] != 'u' && fmt[i] != 'x') 570 return -EINVAL; 571 572 params[fmt_cnt] = args[fmt_cnt]; 573 fmt_cnt++; 574 } 575 576 /* Maximumly we can have MAX_SEQ_PRINTF_VARARGS parameter, just give 577 * all of them to seq_printf(). 578 */ 579 seq_printf(m, fmt, params[0], params[1], params[2], params[3], 580 params[4], params[5], params[6], params[7], params[8], 581 params[9], params[10], params[11]); 582 583 return seq_has_overflowed(m) ? -EOVERFLOW : 0; 584 } 585 --- 0-DAY CI Kernel Test Service, Intel Corporation https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
On 4/27/20 11:02 PM, kbuild test robot wrote: > Hi Yonghong, > > I love your patch! Perhaps something to improve: > > [auto build test WARNING on bpf-next/master] > [cannot apply to bpf/master net/master vhost/linux-next net-next/master linus/master v5.7-rc3 next-20200424] > [if your patch is applied to the wrong git tree, please drop us a note to help > improve the system. BTW, we also suggest to use '--base' option to specify the > base tree in git format-patch, please see https://urldefense.proofpoint.com/v2/url?u=https-3A__stackoverflow.com_a_37406982&d=DwIBAg&c=5VD0RTtNlTh3ycd41b3MUw&r=DA8e1B5r073vIqRrFz7MRA&m=ecuvAWhErc8x32mTscXvNhgSPkwcM7tK05lEVYIQMbI&s=rUkkN8hfXpHttD7t9NCfe5OIFTZZ_cn_SQTDjvs1cj0&e= ] > > url: https://github.com/0day-ci/linux/commits/Yonghong-Song/bpf-implement-bpf-iterator-for-kernel-data/20200428-115101 > base: https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git master > config: sh-allmodconfig (attached as .config) > compiler: sh4-linux-gcc (GCC) 9.3.0 > reproduce: > wget https://urldefense.proofpoint.com/v2/url?u=https-3A__raw.githubusercontent.com_intel_lkp-2Dtests_master_sbin_make.cross&d=DwIBAg&c=5VD0RTtNlTh3ycd41b3MUw&r=DA8e1B5r073vIqRrFz7MRA&m=ecuvAWhErc8x32mTscXvNhgSPkwcM7tK05lEVYIQMbI&s=mm3zd05JFgyD1Fvvg5yehcYq7d9KLZkN7XSYyLaJRkA&e= -O ~/bin/make.cross > chmod +x ~/bin/make.cross > # save the attached .config to linux build tree > COMPILER_INSTALL_PATH=$HOME/0day GCC_VERSION=9.3.0 make.cross ARCH=sh > > If you fix the issue, kindly add following tag as appropriate > Reported-by: kbuild test robot <lkp@intel.com> > > All warnings (new ones prefixed by >>): > > In file included from kernel/trace/bpf_trace.c:10: > kernel/trace/bpf_trace.c: In function 'bpf_seq_printf': >>> kernel/trace/bpf_trace.c:463:35: warning: the frame size of 1672 bytes is larger than 1024 bytes [-Wframe-larger-than=] > 463 | BPF_CALL_5(bpf_seq_printf, struct seq_file *, m, char *, fmt, u32, fmt_size, Thanks for reporting. Currently, I am supporting up to 12 string format specifiers and each string up to 128 bytes. To avoid racing and helper memory allocation, I put it on stack hence the above 1672 bytes, but practically, I think support 4 strings with 128 bytes each is enough. I will make a change in the next revision. > | ^~~~~~~~ > include/linux/filter.h:456:30: note: in definition of macro '__BPF_CAST' > 456 | (unsigned long)0, (t)0))) a > | ^ >>> include/linux/filter.h:449:27: note: in expansion of macro '__BPF_MAP_5' > 449 | #define __BPF_MAP(n, ...) __BPF_MAP_##n(__VA_ARGS__) > | ^~~~~~~~~~ >>> include/linux/filter.h:474:35: note: in expansion of macro '__BPF_MAP' > 474 | return ((btf_##name)____##name)(__BPF_MAP(x,__BPF_CAST,__BPF_N,__VA_ARGS__));\ > | ^~~~~~~~~ >>> include/linux/filter.h:484:31: note: in expansion of macro 'BPF_CALL_x' > 484 | #define BPF_CALL_5(name, ...) BPF_CALL_x(5, name, __VA_ARGS__) > | ^~~~~~~~~~ >>> kernel/trace/bpf_trace.c:463:1: note: in expansion of macro 'BPF_CALL_5' > 463 | BPF_CALL_5(bpf_seq_printf, struct seq_file *, m, char *, fmt, u32, fmt_size, > | ^~~~~~~~~~ > > vim +463 kernel/trace/bpf_trace.c > > 462 > > 463 BPF_CALL_5(bpf_seq_printf, struct seq_file *, m, char *, fmt, u32, fmt_size, > 464 const void *, data, u32, data_len) > 465 { > 466 char bufs[MAX_SEQ_PRINTF_VARARGS][MAX_SEQ_PRINTF_STR_LEN]; > 467 u64 params[MAX_SEQ_PRINTF_VARARGS]; > 468 int i, copy_size, num_args; > 469 const u64 *args = data; > 470 int fmt_cnt = 0; > 471 [...]
On Tue, Apr 28, 2020 at 9:36 AM Yonghong Song <yhs@fb.com> wrote: > > > > On 4/27/20 11:02 PM, kbuild test robot wrote: > > Hi Yonghong, > > > > I love your patch! Perhaps something to improve: > > > > [auto build test WARNING on bpf-next/master] > > [cannot apply to bpf/master net/master vhost/linux-next net-next/master linus/master v5.7-rc3 next-20200424] > > [if your patch is applied to the wrong git tree, please drop us a note to help > > improve the system. BTW, we also suggest to use '--base' option to specify the > > base tree in git format-patch, please see https://urldefense.proofpoint.com/v2/url?u=https-3A__stackoverflow.com_a_37406982&d=DwIBAg&c=5VD0RTtNlTh3ycd41b3MUw&r=DA8e1B5r073vIqRrFz7MRA&m=ecuvAWhErc8x32mTscXvNhgSPkwcM7tK05lEVYIQMbI&s=rUkkN8hfXpHttD7t9NCfe5OIFTZZ_cn_SQTDjvs1cj0&e= ] > > > > url: https://github.com/0day-ci/linux/commits/Yonghong-Song/bpf-implement-bpf-iterator-for-kernel-data/20200428-115101 > > base: https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git master > > config: sh-allmodconfig (attached as .config) > > compiler: sh4-linux-gcc (GCC) 9.3.0 > > reproduce: > > wget https://urldefense.proofpoint.com/v2/url?u=https-3A__raw.githubusercontent.com_intel_lkp-2Dtests_master_sbin_make.cross&d=DwIBAg&c=5VD0RTtNlTh3ycd41b3MUw&r=DA8e1B5r073vIqRrFz7MRA&m=ecuvAWhErc8x32mTscXvNhgSPkwcM7tK05lEVYIQMbI&s=mm3zd05JFgyD1Fvvg5yehcYq7d9KLZkN7XSYyLaJRkA&e= -O ~/bin/make.cross > > chmod +x ~/bin/make.cross > > # save the attached .config to linux build tree > > COMPILER_INSTALL_PATH=$HOME/0day GCC_VERSION=9.3.0 make.cross ARCH=sh > > > > If you fix the issue, kindly add following tag as appropriate > > Reported-by: kbuild test robot <lkp@intel.com> > > > > All warnings (new ones prefixed by >>): > > > > In file included from kernel/trace/bpf_trace.c:10: > > kernel/trace/bpf_trace.c: In function 'bpf_seq_printf': > >>> kernel/trace/bpf_trace.c:463:35: warning: the frame size of 1672 bytes is larger than 1024 bytes [-Wframe-larger-than=] > > 463 | BPF_CALL_5(bpf_seq_printf, struct seq_file *, m, char *, fmt, u32, fmt_size, > > Thanks for reporting. Currently, I am supporting up to 12 string format > specifiers and each string up to 128 bytes. To avoid racing and helper > memory allocation, I put it on stack hence the above 1672 bytes, but > practically, I think support 4 strings with 128 bytes each is enough. > I will make a change in the next revision. It's still quite a lot of data on stack. How about per-CPU buffer that this function can use for temporary storage? > > > | ^~~~~~~~ > > include/linux/filter.h:456:30: note: in definition of macro '__BPF_CAST' > > 456 | (unsigned long)0, (t)0))) a > > | ^ > >>> include/linux/filter.h:449:27: note: in expansion of macro '__BPF_MAP_5' > > 449 | #define __BPF_MAP(n, ...) __BPF_MAP_##n(__VA_ARGS__) > > | ^~~~~~~~~~ > >>> include/linux/filter.h:474:35: note: in expansion of macro '__BPF_MAP' > > 474 | return ((btf_##name)____##name)(__BPF_MAP(x,__BPF_CAST,__BPF_N,__VA_ARGS__));\ > > | ^~~~~~~~~ > >>> include/linux/filter.h:484:31: note: in expansion of macro 'BPF_CALL_x' > > 484 | #define BPF_CALL_5(name, ...) BPF_CALL_x(5, name, __VA_ARGS__) > > | ^~~~~~~~~~ > >>> kernel/trace/bpf_trace.c:463:1: note: in expansion of macro 'BPF_CALL_5' > > 463 | BPF_CALL_5(bpf_seq_printf, struct seq_file *, m, char *, fmt, u32, fmt_size, > > | ^~~~~~~~~~ > > > > vim +463 kernel/trace/bpf_trace.c > > > > 462 > > > 463 BPF_CALL_5(bpf_seq_printf, struct seq_file *, m, char *, fmt, u32, fmt_size, > > 464 const void *, data, u32, data_len) > > 465 { > > 466 char bufs[MAX_SEQ_PRINTF_VARARGS][MAX_SEQ_PRINTF_STR_LEN]; > > 467 u64 params[MAX_SEQ_PRINTF_VARARGS]; > > 468 int i, copy_size, num_args; > > 469 const u64 *args = data; > > 470 int fmt_cnt = 0; > > 471 > [...]
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 576651110d16..f0ab17d8fb73 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -3042,6 +3042,33 @@ union bpf_attr { * See: clock_gettime(CLOCK_BOOTTIME) * Return * Current *ktime*. + * + * int bpf_seq_printf(struct seq_file *m, const char *fmt, u32 fmt_size, const void *data, u32 data_len) + * Description + * seq_printf uses seq_file seq_printf() to print out the format string. + * The *m* represents the seq_file. The *fmt* and *fmt_size* are for + * the format string itself. The *data* and *data_len* are format string + * arguments. The *data* are a u64 array and corresponding format string + * values are stored in the array. For strings and pointers where pointees + * are accessed, only the pointer values are stored in the *data* array. + * The *data_len* is the *data* size in term of bytes. + * Return + * 0 on success, or a negative errno in case of failure. + * + * * **-EINVAL** Invalid arguments, or invalid/unsupported formats. + * * **-E2BIG** Too many format specifiers. + * * **-ENOMEM** No enough memory to copy pointees or strings. + * * **-EOVERFLOW** Overflow happens, the same object will be tried again. + * + * int bpf_seq_write(struct seq_file *m, const void *data, u32 len) + * Description + * seq_write uses seq_file seq_write() to write the data. + * The *m* represents the seq_file. The *data* and *len* represent the + * data to write in bytes. + * Return + * 0 on success, or a negative errno in case of failure. + * + * * **-EOVERFLOW** Overflow happens, the same object will be tried again. */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ @@ -3169,7 +3196,9 @@ union bpf_attr { FN(get_netns_cookie), \ FN(get_current_ancestor_cgroup_id), \ FN(sk_assign), \ - FN(ktime_get_boot_ns), + FN(ktime_get_boot_ns), \ + FN(seq_printf), \ + FN(seq_write), /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index e875c95d3ced..f7c5587b5d2e 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -457,6 +457,161 @@ const struct bpf_func_proto *bpf_get_trace_printk_proto(void) return &bpf_trace_printk_proto; } +#define MAX_SEQ_PRINTF_VARARGS 12 +#define MAX_SEQ_PRINTF_STR_LEN 128 + +BPF_CALL_5(bpf_seq_printf, struct seq_file *, m, char *, fmt, u32, fmt_size, + const void *, data, u32, data_len) +{ + char bufs[MAX_SEQ_PRINTF_VARARGS][MAX_SEQ_PRINTF_STR_LEN]; + u64 params[MAX_SEQ_PRINTF_VARARGS]; + int i, copy_size, num_args; + const u64 *args = data; + int fmt_cnt = 0; + + /* + * bpf_check()->check_func_arg()->check_stack_boundary() + * guarantees that fmt points to bpf program stack, + * fmt_size bytes of it were initialized and fmt_size > 0 + */ + if (fmt[--fmt_size] != 0) + return -EINVAL; + + if (data_len & 7) + return -EINVAL; + + for (i = 0; i < fmt_size; i++) { + if (fmt[i] == '%' && (!data || !data_len)) + return -EINVAL; + } + + num_args = data_len / 8; + + /* check format string for allowed specifiers */ + for (i = 0; i < fmt_size; i++) { + if ((!isprint(fmt[i]) && !isspace(fmt[i])) || !isascii(fmt[i])) + return -EINVAL; + + if (fmt[i] != '%') + continue; + + if (fmt_cnt >= MAX_SEQ_PRINTF_VARARGS) + return -E2BIG; + + if (fmt_cnt >= num_args) + return -EINVAL; + + /* fmt[i] != 0 && fmt[last] == 0, so we can access fmt[i + 1] */ + i++; + + /* skip optional "[0+-][num]" width formating field */ + while (fmt[i] == '0' || fmt[i] == '+' || fmt[i] == '-') + i++; + if (fmt[i] >= '1' && fmt[i] <= '9') { + i++; + while (fmt[i] >= '0' && fmt[i] <= '9') + i++; + } + + if (fmt[i] == 's') { + /* disallow any further format extensions */ + if (fmt[i + 1] != 0 && + !isspace(fmt[i + 1]) && + !ispunct(fmt[i + 1])) + return -EINVAL; + + /* try our best to copy */ + bufs[fmt_cnt][0] = 0; + strncpy_from_unsafe(bufs[fmt_cnt], + (void *) (long) args[fmt_cnt], + MAX_SEQ_PRINTF_STR_LEN); + params[fmt_cnt] = (u64)(long)bufs[fmt_cnt]; + + fmt_cnt++; + continue; + } + + if (fmt[i] == 'p') { + if (fmt[i + 1] == 0 || + fmt[i + 1] == 'K' || + fmt[i + 1] == 'x') { + /* just kernel pointers */ + params[fmt_cnt] = args[fmt_cnt]; + fmt_cnt++; + continue; + } + + /* only support "%pI4", "%pi4", "%pI6" and "pi6". */ + if (fmt[i + 1] != 'i' && fmt[i + 1] != 'I') + return -EINVAL; + if (fmt[i + 2] != '4' && fmt[i + 2] != '6') + return -EINVAL; + + copy_size = (fmt[i + 2] == '4') ? 4 : 16; + + /* try our best to copy */ + probe_kernel_read(bufs[fmt_cnt], + (void *) (long) args[fmt_cnt], copy_size); + params[fmt_cnt] = (u64)(long)bufs[fmt_cnt]; + + i += 2; + fmt_cnt++; + continue; + } + + if (fmt[i] == 'l') { + i++; + if (fmt[i] == 'l') + i++; + } + + if (fmt[i] != 'i' && fmt[i] != 'd' && + fmt[i] != 'u' && fmt[i] != 'x') + return -EINVAL; + + params[fmt_cnt] = args[fmt_cnt]; + fmt_cnt++; + } + + /* Maximumly we can have MAX_SEQ_PRINTF_VARARGS parameter, just give + * all of them to seq_printf(). + */ + seq_printf(m, fmt, params[0], params[1], params[2], params[3], + params[4], params[5], params[6], params[7], params[8], + params[9], params[10], params[11]); + + return seq_has_overflowed(m) ? -EOVERFLOW : 0; +} + +static int bpf_seq_printf_btf_ids[5]; +static const struct bpf_func_proto bpf_seq_printf_proto = { + .func = bpf_seq_printf, + .gpl_only = true, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_BTF_ID, + .arg2_type = ARG_PTR_TO_MEM, + .arg3_type = ARG_CONST_SIZE, + .arg4_type = ARG_PTR_TO_MEM_OR_NULL, + .arg5_type = ARG_CONST_SIZE_OR_ZERO, + .btf_id = bpf_seq_printf_btf_ids, +}; + +BPF_CALL_3(bpf_seq_write, struct seq_file *, m, const void *, data, u32, len) +{ + return seq_write(m, data, len) ? -EOVERFLOW : 0; +} + +static int bpf_seq_write_btf_ids[5]; +static const struct bpf_func_proto bpf_seq_write_proto = { + .func = bpf_seq_write, + .gpl_only = true, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_BTF_ID, + .arg2_type = ARG_PTR_TO_MEM, + .arg3_type = ARG_CONST_SIZE, + .btf_id = bpf_seq_write_btf_ids, +}; + static __always_inline int get_map_perf_counter(struct bpf_map *map, u64 flags, u64 *value, u64 *enabled, u64 *running) @@ -1226,6 +1381,10 @@ tracing_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) case BPF_FUNC_xdp_output: return &bpf_xdp_output_proto; #endif + case BPF_FUNC_seq_printf: + return &bpf_seq_printf_proto; + case BPF_FUNC_seq_write: + return &bpf_seq_write_proto; default: return raw_tp_prog_func_proto(func_id, prog); } diff --git a/scripts/bpf_helpers_doc.py b/scripts/bpf_helpers_doc.py index f43d193aff3a..ded304c96a05 100755 --- a/scripts/bpf_helpers_doc.py +++ b/scripts/bpf_helpers_doc.py @@ -414,6 +414,7 @@ class PrinterHelpers(Printer): 'struct sk_reuseport_md', 'struct sockaddr', 'struct tcphdr', + 'struct seq_file', 'struct __sk_buff', 'struct sk_msg_md', @@ -450,6 +451,7 @@ class PrinterHelpers(Printer): 'struct sk_reuseport_md', 'struct sockaddr', 'struct tcphdr', + 'struct seq_file', } mapped_types = { 'u8': '__u8', diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 576651110d16..f0ab17d8fb73 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -3042,6 +3042,33 @@ union bpf_attr { * See: clock_gettime(CLOCK_BOOTTIME) * Return * Current *ktime*. + * + * int bpf_seq_printf(struct seq_file *m, const char *fmt, u32 fmt_size, const void *data, u32 data_len) + * Description + * seq_printf uses seq_file seq_printf() to print out the format string. + * The *m* represents the seq_file. The *fmt* and *fmt_size* are for + * the format string itself. The *data* and *data_len* are format string + * arguments. The *data* are a u64 array and corresponding format string + * values are stored in the array. For strings and pointers where pointees + * are accessed, only the pointer values are stored in the *data* array. + * The *data_len* is the *data* size in term of bytes. + * Return + * 0 on success, or a negative errno in case of failure. + * + * * **-EINVAL** Invalid arguments, or invalid/unsupported formats. + * * **-E2BIG** Too many format specifiers. + * * **-ENOMEM** No enough memory to copy pointees or strings. + * * **-EOVERFLOW** Overflow happens, the same object will be tried again. + * + * int bpf_seq_write(struct seq_file *m, const void *data, u32 len) + * Description + * seq_write uses seq_file seq_write() to write the data. + * The *m* represents the seq_file. The *data* and *len* represent the + * data to write in bytes. + * Return + * 0 on success, or a negative errno in case of failure. + * + * * **-EOVERFLOW** Overflow happens, the same object will be tried again. */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ @@ -3169,7 +3196,9 @@ union bpf_attr { FN(get_netns_cookie), \ FN(get_current_ancestor_cgroup_id), \ FN(sk_assign), \ - FN(ktime_get_boot_ns), + FN(ktime_get_boot_ns), \ + FN(seq_printf), \ + FN(seq_write), /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call
Two helpers bpf_seq_printf and bpf_seq_write, are added for writing data to the seq_file buffer. bpf_seq_printf supports common format string flag/width/type fields so at least I can get identical results for netlink and ipv6_route targets. For bpf_seq_printf and bpf_seq_write, return value -EOVERFLOW specifically indicates a write failure due to overflow, which means the object will be repeated in the next bpf invocation if object collection stays the same. Note that if the object collection is changed, depending how collection traversal is done, even if the object still in the collection, it may not be visited. Signed-off-by: Yonghong Song <yhs@fb.com> --- include/uapi/linux/bpf.h | 31 ++++++- kernel/trace/bpf_trace.c | 159 +++++++++++++++++++++++++++++++++ scripts/bpf_helpers_doc.py | 2 + tools/include/uapi/linux/bpf.h | 31 ++++++- 4 files changed, 221 insertions(+), 2 deletions(-)