diff mbox series

[v7,perf,bpf,02/15] bpf: libbpf: introduce bpf_program__get_prog_info_linear()

Message ID 20190307175810.249857-3-songliubraving@fb.com
State Changes Requested
Delegated to: BPF Maintainers
Headers show
Series perf annotation of BPF programs | expand

Commit Message

Song Liu March 7, 2019, 5:57 p.m. UTC
Currently, bpf_prog_info includes 9 arrays. The user has the option to
fetch any combination of these arrays. However, this requires a lot of
handling of these arrays. This work becomes more tricky when we need to
store bpf_prog_info to a file, because these arrays are allocated
independently.

This patch introduces struct bpf_prog_info_linear, which stores arrays
of bpf_prog_info in continues memory. Helper functions are introduced
to unify the work to get different information of bpf_prog_info.
Specifically, bpf_program__get_prog_info_linear() allows the user to
select which arrays to fetch, and handles details for the user.

Plesae see the comments before enum bpf_prog_info_array for more details
and examples.

Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Song Liu <songliubraving@fb.com>
---
 tools/lib/bpf/libbpf.c   | 251 +++++++++++++++++++++++++++++++++++++++
 tools/lib/bpf/libbpf.h   |  63 ++++++++++
 tools/lib/bpf/libbpf.map |   3 +
 3 files changed, 317 insertions(+)

Comments

Arnaldo Carvalho de Melo March 11, 2019, 6:26 p.m. UTC | #1
Em Thu, Mar 07, 2019 at 09:57:57AM -0800, Song Liu escreveu:
> Currently, bpf_prog_info includes 9 arrays. The user has the option to
> fetch any combination of these arrays. However, this requires a lot of
> handling of these arrays. This work becomes more tricky when we need to
> store bpf_prog_info to a file, because these arrays are allocated
> independently.
> 
> This patch introduces struct bpf_prog_info_linear, which stores arrays
> of bpf_prog_info in continues memory. Helper functions are introduced
> to unify the work to get different information of bpf_prog_info.
> Specifically, bpf_program__get_prog_info_linear() allows the user to
> select which arrays to fetch, and handles details for the user.
> 
> Plesae see the comments before enum bpf_prog_info_array for more details
> and examples.
> 
> Cc: Daniel Borkmann <daniel@iogearbox.net>
> Cc: Alexei Starovoitov <ast@kernel.org>

Daniel, are you ok with these changes to libbpf and bpftool? Perhaps
those should be detached from this patchkit and submitted sooner,
eroding the size of this kit.

Alternatively, if you're ok with it, please provide your Acked-by and
I'll process as soon as I get back to it after Jiri is done reviewing.

- Arnaldo

> Signed-off-by: Song Liu <songliubraving@fb.com>
> ---
>  tools/lib/bpf/libbpf.c   | 251 +++++++++++++++++++++++++++++++++++++++
>  tools/lib/bpf/libbpf.h   |  63 ++++++++++
>  tools/lib/bpf/libbpf.map |   3 +
>  3 files changed, 317 insertions(+)
> 
> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index f5eb60379c8d..ca00ce5cbae0 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c
> @@ -112,6 +112,11 @@ void libbpf_print(enum libbpf_print_level level, const char *format, ...)
>  # define LIBBPF_ELF_C_READ_MMAP ELF_C_READ
>  #endif
>  
> +static inline __u64 ptr_to_u64(const void *ptr)
> +{
> +	return (__u64) (unsigned long) ptr;
> +}
> +
>  struct bpf_capabilities {
>  	/* v4.14: kernel support for program & map names. */
>  	__u32 name:1;
> @@ -2997,3 +3002,249 @@ bpf_perf_event_read_simple(void *mmap_mem, size_t mmap_size, size_t page_size,
>  	ring_buffer_write_tail(header, data_tail);
>  	return ret;
>  }
> +
> +struct bpf_prog_info_array_desc {
> +	int	array_offset;	/* e.g. offset of jited_prog_insns */
> +	int	count_offset;	/* e.g. offset of jited_prog_len */
> +	int	size_offset;	/* > 0: offset of rec size,
> +				 * < 0: fix size of -size_offset
> +				 */
> +};
> +
> +static struct bpf_prog_info_array_desc bpf_prog_info_array_desc[] = {
> +	[BPF_PROG_INFO_JITED_INSNS] = {
> +		offsetof(struct bpf_prog_info, jited_prog_insns),
> +		offsetof(struct bpf_prog_info, jited_prog_len),
> +		-1,
> +	},
> +	[BPF_PROG_INFO_XLATED_INSNS] = {
> +		offsetof(struct bpf_prog_info, xlated_prog_insns),
> +		offsetof(struct bpf_prog_info, xlated_prog_len),
> +		-1,
> +	},
> +	[BPF_PROG_INFO_MAP_IDS] = {
> +		offsetof(struct bpf_prog_info, map_ids),
> +		offsetof(struct bpf_prog_info, nr_map_ids),
> +		-(int)sizeof(__u32),
> +	},
> +	[BPF_PROG_INFO_JITED_KSYMS] = {
> +		offsetof(struct bpf_prog_info, jited_ksyms),
> +		offsetof(struct bpf_prog_info, nr_jited_ksyms),
> +		-(int)sizeof(__u64),
> +	},
> +	[BPF_PROG_INFO_JITED_FUNC_LENS] = {
> +		offsetof(struct bpf_prog_info, jited_func_lens),
> +		offsetof(struct bpf_prog_info, nr_jited_func_lens),
> +		-(int)sizeof(__u32),
> +	},
> +	[BPF_PROG_INFO_FUNC_INFO] = {
> +		offsetof(struct bpf_prog_info, func_info),
> +		offsetof(struct bpf_prog_info, nr_func_info),
> +		offsetof(struct bpf_prog_info, func_info_rec_size),
> +	},
> +	[BPF_PROG_INFO_LINE_INFO] = {
> +		offsetof(struct bpf_prog_info, line_info),
> +		offsetof(struct bpf_prog_info, nr_line_info),
> +		offsetof(struct bpf_prog_info, line_info_rec_size),
> +	},
> +	[BPF_PROG_INFO_JITED_LINE_INFO] = {
> +		offsetof(struct bpf_prog_info, jited_line_info),
> +		offsetof(struct bpf_prog_info, nr_jited_line_info),
> +		offsetof(struct bpf_prog_info, jited_line_info_rec_size),
> +	},
> +	[BPF_PROG_INFO_PROG_TAGS] = {
> +		offsetof(struct bpf_prog_info, prog_tags),
> +		offsetof(struct bpf_prog_info, nr_prog_tags),
> +		-(int)sizeof(__u8) * BPF_TAG_SIZE,
> +	},
> +
> +};
> +
> +static __u32 bpf_prog_info_read_offset_u32(struct bpf_prog_info *info, int offset)
> +{
> +	__u32 *array = (__u32 *)info;
> +
> +	if (offset >= 0)
> +		return array[offset / sizeof(__u32)];
> +	return -(int)offset;
> +}
> +
> +static __u64 bpf_prog_info_read_offset_u64(struct bpf_prog_info *info, int offset)
> +{
> +	__u64 *array = (__u64 *)info;
> +
> +	if (offset >= 0)
> +		return array[offset / sizeof(__u64)];
> +	return -(int)offset;
> +}
> +
> +static void bpf_prog_info_set_offset_u32(struct bpf_prog_info *info, int offset,
> +					 __u32 val)
> +{
> +	__u32 *array = (__u32 *)info;
> +
> +	if (offset >= 0)
> +		array[offset / sizeof(__u32)] = val;
> +}
> +
> +static void bpf_prog_info_set_offset_u64(struct bpf_prog_info *info, int offset,
> +					 __u64 val)
> +{
> +	__u64 *array = (__u64 *)info;
> +
> +	if (offset >= 0)
> +		array[offset / sizeof(__u64)] = val;
> +}
> +
> +struct bpf_prog_info_linear *
> +bpf_program__get_prog_info_linear(int fd, __u64 arrays)
> +{
> +	struct bpf_prog_info_linear *info_linear;
> +	struct bpf_prog_info info = {};
> +	__u32 info_len = sizeof(info);
> +	__u32 data_len = 0;
> +	int i, err;
> +	void *ptr;
> +
> +	if (arrays >> BPF_PROG_INFO_LAST_ARRAY)
> +		return ERR_PTR(-EINVAL);
> +
> +	/* step 1: get array dimensions */
> +	err = bpf_obj_get_info_by_fd(fd, &info, &info_len);
> +	if (err) {
> +		pr_debug("can't get prog info: %s", strerror(errno));
> +		return ERR_PTR(-EFAULT);
> +	}
> +
> +	/* step 2: calculate total size of all arrays */
> +	for (i = BPF_PROG_INFO_FIRST_ARRAY; i < BPF_PROG_INFO_LAST_ARRAY; ++i) {
> +		bool include_array = (arrays & (1UL << i)) > 0;
> +		struct bpf_prog_info_array_desc *desc;
> +		__u32 count, size;
> +
> +		desc = bpf_prog_info_array_desc + i;
> +
> +		/* kernel is too old to support this field */
> +		if (info_len < desc->array_offset + sizeof(__u32) ||
> +		    info_len < desc->count_offset + sizeof(__u32) ||
> +		    (desc->size_offset > 0 && info_len < desc->size_offset))
> +			include_array = false;
> +
> +		if (!include_array) {
> +			arrays &= ~(1UL << i);	/* clear the bit */
> +			continue;
> +		}
> +
> +		count = bpf_prog_info_read_offset_u32(&info, desc->count_offset);
> +		size = bpf_prog_info_read_offset_u32(&info, desc->size_offset);
> +
> +		data_len += count * size;
> +	}
> +
> +	/* step 3: allocate continuous memory */
> +	data_len = roundup(data_len, sizeof(__u64));
> +	info_linear = malloc(sizeof(struct bpf_prog_info_linear) + data_len);
> +	if (!info_linear)
> +		return ERR_PTR(-ENOMEM);
> +
> +	/* step 4: fill data to info_linear->info */
> +	info_linear->arrays = arrays;
> +	memset(&info_linear->info, 0, sizeof(info));
> +	ptr = info_linear->data;
> +
> +	for (i = BPF_PROG_INFO_FIRST_ARRAY; i < BPF_PROG_INFO_LAST_ARRAY; ++i) {
> +		struct bpf_prog_info_array_desc *desc;
> +		__u32 count, size;
> +
> +		if ((arrays & (1UL << i)) == 0)
> +			continue;
> +
> +		desc = bpf_prog_info_array_desc + i;
> +		count = bpf_prog_info_read_offset_u32(&info, desc->count_offset);
> +		size = bpf_prog_info_read_offset_u32(&info, desc->size_offset);
> +		bpf_prog_info_set_offset_u32(&info_linear->info,
> +					     desc->count_offset, count);
> +		bpf_prog_info_set_offset_u32(&info_linear->info,
> +					     desc->size_offset, size);
> +		bpf_prog_info_set_offset_u64(&info_linear->info,
> +					     desc->array_offset,
> +					     ptr_to_u64(ptr));
> +		ptr += count * size;
> +	}
> +
> +	/* step 5: call syscall again to get required arrays */
> +	err = bpf_obj_get_info_by_fd(fd, &info_linear->info, &info_len);
> +	if (err) {
> +		pr_debug("can't get prog info: %s", strerror(errno));
> +		free(info_linear);
> +		return ERR_PTR(-EFAULT);
> +	}
> +
> +	/* step 6: verify the data */
> +	for (i = BPF_PROG_INFO_FIRST_ARRAY; i < BPF_PROG_INFO_LAST_ARRAY; ++i) {
> +		struct bpf_prog_info_array_desc *desc;
> +		__u32 v1, v2;
> +
> +		if ((arrays & (1UL << i)) == 0)
> +			continue;
> +
> +		desc = bpf_prog_info_array_desc + i;
> +		v1 = bpf_prog_info_read_offset_u32(&info, desc->count_offset);
> +		v2 = bpf_prog_info_read_offset_u32(&info_linear->info,
> +						   desc->count_offset);
> +		if (v1 != v2)
> +			pr_warning("%s: mismatch in element count\n", __func__);
> +
> +		v1 = bpf_prog_info_read_offset_u32(&info, desc->size_offset);
> +		v2 = bpf_prog_info_read_offset_u32(&info_linear->info,
> +						   desc->size_offset);
> +		if (v1 != v2)
> +			pr_warning("%s: mismatch in rec size\n", __func__);
> +	}
> +
> +	/* step 7: update info_len and data_len */
> +	info_linear->info_len = sizeof(struct bpf_prog_info);
> +	info_linear->data_len = data_len;
> +
> +	return info_linear;
> +}
> +
> +void bpf_program__bpil_addr_to_offs(struct bpf_prog_info_linear *info_linear)
> +{
> +	int i;
> +
> +	for (i = BPF_PROG_INFO_FIRST_ARRAY; i < BPF_PROG_INFO_LAST_ARRAY; ++i) {
> +		struct bpf_prog_info_array_desc *desc;
> +		__u64 addr, offs;
> +
> +		if ((info_linear->arrays & (1UL << i)) == 0)
> +			continue;
> +
> +		desc = bpf_prog_info_array_desc + i;
> +		addr = bpf_prog_info_read_offset_u64(&info_linear->info,
> +						     desc->array_offset);
> +		offs = addr - ptr_to_u64(info_linear->data);
> +		bpf_prog_info_set_offset_u64(&info_linear->info,
> +					     desc->array_offset, offs);
> +	}
> +}
> +
> +void bpf_program__bpil_offs_to_addr(struct bpf_prog_info_linear *info_linear)
> +{
> +	int i;
> +
> +	for (i = BPF_PROG_INFO_FIRST_ARRAY; i < BPF_PROG_INFO_LAST_ARRAY; ++i) {
> +		struct bpf_prog_info_array_desc *desc;
> +		__u64 addr, offs;
> +
> +		if ((info_linear->arrays & (1UL << i)) == 0)
> +			continue;
> +
> +		desc = bpf_prog_info_array_desc + i;
> +		offs = bpf_prog_info_read_offset_u64(&info_linear->info,
> +						     desc->array_offset);
> +		addr = offs + ptr_to_u64(info_linear->data);
> +		bpf_prog_info_set_offset_u64(&info_linear->info,
> +					     desc->array_offset, addr);
> +	}
> +}
> diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
> index b4652aa1a58a..c7645a5e1ac0 100644
> --- a/tools/lib/bpf/libbpf.h
> +++ b/tools/lib/bpf/libbpf.h
> @@ -377,6 +377,69 @@ LIBBPF_API bool bpf_probe_map_type(enum bpf_map_type map_type, __u32 ifindex);
>  LIBBPF_API bool bpf_probe_helper(enum bpf_func_id id,
>  				 enum bpf_prog_type prog_type, __u32 ifindex);
>  
> +/*
> + * Get bpf_prog_info in continuous memory
> + *
> + * struct bpf_prog_info has multiple arrays. The user has option to choose
> + * arrays to fetch from kernel. The following APIs provide uniform way to
> + * fetch these data. All arrays in bpf_prog_info are stored in singile
> + * continuous memory region. This makes it easy to store the info in a
> + * file.
> + *
> + * Before writing bpf_prog_info_linear to files, it is necessary to
> + * translate pointers bpf_prog_info to offsets. Helper functions
> + * bpf_program__bpil_addr_to_offs() and bpf_program__bpil_offs_to_addr()
> + * are introduced to switch between pointers and offsets.
> + *
> + * Examples:
> + *   # To fetch map_ids and prog_tags:
> + *   __u64 arrays = (1UL << BPF_PROG_INFO_MAP_IDS) |
> + *           (1UL << BPF_PROG_INFO_PROG_TAGS);
> + *   struct bpf_prog_info_linear *info_linear =
> + *           bpf_program__get_prog_info_linear(fd, arrays);
> + *
> + *   # To save data in file
> + *   bpf_program__bpil_addr_to_offs(info_linear);
> + *   write(f, info_linear, sizeof(*info_linear) + info_linear->data_len);
> + *
> + *   # To read data from file
> + *   read(f, info_linear, <proper_size>);
> + *   bpf_program__bpil_offs_to_addr(info_linear);
> + */
> +enum bpf_prog_info_array {
> +	BPF_PROG_INFO_FIRST_ARRAY = 0,
> +	BPF_PROG_INFO_JITED_INSNS = 0,
> +	BPF_PROG_INFO_XLATED_INSNS,
> +	BPF_PROG_INFO_MAP_IDS,
> +	BPF_PROG_INFO_JITED_KSYMS,
> +	BPF_PROG_INFO_JITED_FUNC_LENS,
> +	BPF_PROG_INFO_FUNC_INFO,
> +	BPF_PROG_INFO_LINE_INFO,
> +	BPF_PROG_INFO_JITED_LINE_INFO,
> +	BPF_PROG_INFO_PROG_TAGS,
> +	BPF_PROG_INFO_LAST_ARRAY,
> +};
> +
> +struct bpf_prog_info_linear {
> +	/* size of struct bpf_prog_info, when the tool is compiled */
> +	__u32			info_len;
> +	/* total bytes allocated for data, round up to 8 bytes */
> +	__u32			data_len;
> +	/* which arrays are included in data */
> +	__u64			arrays;
> +	struct bpf_prog_info	info;
> +	__u8			data[];
> +};
> +
> +LIBBPF_API struct bpf_prog_info_linear *
> +bpf_program__get_prog_info_linear(int fd, __u64 arrays);
> +
> +LIBBPF_API void
> +bpf_program__bpil_addr_to_offs(struct bpf_prog_info_linear *info_linear);
> +
> +LIBBPF_API void
> +bpf_program__bpil_offs_to_addr(struct bpf_prog_info_linear *info_linear);
> +
>  #ifdef __cplusplus
>  } /* extern "C" */
>  #endif
> diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
> index 778a26702a70..f3ce50500cf2 100644
> --- a/tools/lib/bpf/libbpf.map
> +++ b/tools/lib/bpf/libbpf.map
> @@ -153,4 +153,7 @@ LIBBPF_0.0.2 {
>  		xsk_socket__delete;
>  		xsk_umem__fd;
>  		xsk_socket__fd;
> +		bpf_program__get_prog_info_linear;
> +		bpf_program__bpil_addr_to_offs;
> +		bpf_program__bpil_offs_to_addr;
>  } LIBBPF_0.0.1;
> -- 
> 2.17.1
Daniel Borkmann March 11, 2019, 8:45 p.m. UTC | #2
On 03/11/2019 07:26 PM, Arnaldo Carvalho de Melo wrote:
> Em Thu, Mar 07, 2019 at 09:57:57AM -0800, Song Liu escreveu:
>> Currently, bpf_prog_info includes 9 arrays. The user has the option to
>> fetch any combination of these arrays. However, this requires a lot of
>> handling of these arrays. This work becomes more tricky when we need to
>> store bpf_prog_info to a file, because these arrays are allocated
>> independently.
>>
>> This patch introduces struct bpf_prog_info_linear, which stores arrays
>> of bpf_prog_info in continues memory. Helper functions are introduced
>> to unify the work to get different information of bpf_prog_info.
>> Specifically, bpf_program__get_prog_info_linear() allows the user to
>> select which arrays to fetch, and handles details for the user.
>>
>> Plesae see the comments before enum bpf_prog_info_array for more details
>> and examples.
>>
>> Cc: Daniel Borkmann <daniel@iogearbox.net>
>> Cc: Alexei Starovoitov <ast@kernel.org>
> 
> Daniel, are you ok with these changes to libbpf and bpftool? Perhaps
> those should be detached from this patchkit and submitted sooner,
> eroding the size of this kit.
> 
> Alternatively, if you're ok with it, please provide your Acked-by and
> I'll process as soon as I get back to it after Jiri is done reviewing.

Overall looks okay. Are you planning to get these in for 5.1 window? If
yes, that would be great, otherwise we might need to cherry-pick the libbpf
and bpftool ones from your tree into bpf-next as well since there's just
too much going on in this area where we'd potentially run into complex
merge conflicts. In the latter case, libbpf.map would need to be fixed up
to LIBBPF_0.0.3 as convention is that this is in line with kernel release.

Thanks,
Daniel

> - Arnaldo
Arnaldo Carvalho de Melo March 11, 2019, 8:57 p.m. UTC | #3
Em Mon, Mar 11, 2019 at 09:45:49PM +0100, Daniel Borkmann escreveu:
> On 03/11/2019 07:26 PM, Arnaldo Carvalho de Melo wrote:
> > Em Thu, Mar 07, 2019 at 09:57:57AM -0800, Song Liu escreveu:
> >> Currently, bpf_prog_info includes 9 arrays. The user has the option to
> >> fetch any combination of these arrays. However, this requires a lot of
> >> handling of these arrays. This work becomes more tricky when we need to
> >> store bpf_prog_info to a file, because these arrays are allocated
> >> independently.
> >>
> >> This patch introduces struct bpf_prog_info_linear, which stores arrays
> >> of bpf_prog_info in continues memory. Helper functions are introduced
> >> to unify the work to get different information of bpf_prog_info.
> >> Specifically, bpf_program__get_prog_info_linear() allows the user to
> >> select which arrays to fetch, and handles details for the user.
> >>
> >> Plesae see the comments before enum bpf_prog_info_array for more details
> >> and examples.
> >>
> >> Cc: Daniel Borkmann <daniel@iogearbox.net>
> >> Cc: Alexei Starovoitov <ast@kernel.org>
> > 
> > Daniel, are you ok with these changes to libbpf and bpftool? Perhaps
> > those should be detached from this patchkit and submitted sooner,
> > eroding the size of this kit.
> > 
> > Alternatively, if you're ok with it, please provide your Acked-by and
> > I'll process as soon as I get back to it after Jiri is done reviewing.
> 
> Overall looks okay. Are you planning to get these in for 5.1 window? If

I hope so.

> yes, that would be great, otherwise we might need to cherry-pick the libbpf

So I'll try and grab those for my next pull req to Ingo, this week,
will add your Acked-by, ok?

> and bpftool ones from your tree into bpf-next as well since there's just
> too much going on in this area where we'd potentially run into complex
> merge conflicts. In the latter case, libbpf.map would need to be fixed up
> to LIBBPF_0.0.3 as convention is that this is in line with kernel release.

BTW, while running my build tests I had to add this to today's batch,
should be trivial when merging, I think, ok?


commit dfcbc2f2994b8a3af3605a26dc29c07ad7378bf4
Author: Arnaldo Carvalho de Melo <acme@redhat.com>
Date:   Mon Mar 11 17:07:52 2019 -0300

    tools lib bpf: Fix the build by adding a missing stdarg.h include
    
    The libbpf_print_fn_t typedef uses va_list without including the header
    where that type is defined, stdarg.h, breaking in places where we're
    unlucky for that type not to be already defined by some previously
    included header.
    
    Noticed while building on fedora 24 cross building tools/perf to the ARC
    architecture using the uClibc C library:
    
      28 fedora:24-x-ARC-uClibc   : FAIL arc-linux-gcc (ARCompact ISA Linux uClibc toolchain 2017.09-rc2) 7.1.1 20170710
    
        CC       /tmp/build/perf/tests/llvm.o
      In file included from tests/llvm.c:3:0:
      /git/linux/tools/lib/bpf/libbpf.h:57:20: error: unknown type name 'va_list'
            const char *, va_list ap);
                          ^~~~~~~
      /git/linux/tools/lib/bpf/libbpf.h:59:34: error: unknown type name 'libbpf_print_fn_t'
       LIBBPF_API void libbpf_set_print(libbpf_print_fn_t fn);
                                        ^~~~~~~~~~~~~~~~~
      mv: cannot stat '/tmp/build/perf/tests/.llvm.o.tmp': No such file or directory
    
    Cc: Alexei Starovoitov <ast@kernel.org>
    Cc: Daniel Borkmann <daniel@iogearbox.net>
    Cc: Jakub Kicinski <jakub.kicinski@netronome.com>
    Cc: Jiri Olsa <jolsa@kernel.org>
    Cc: Namhyung Kim <namhyung@kernel.org>
    Cc: Quentin Monnet <quentin.monnet@netronome.com>
    Cc: Stanislav Fomichev <sdf@google.com>
    Cc: Yonghong Song <yhs@fb.com>
    Fixes: a8a1f7d09cfc ("libbpf: fix libbpf_print")
    Link: https://lkml.kernel.org/n/tip-5270n2quu2gqz22o7itfdx00@git.kernel.org
    Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index b4652aa1a58a..aa1521a51687 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -10,6 +10,7 @@
 #ifndef __LIBBPF_LIBBPF_H
 #define __LIBBPF_LIBBPF_H
 
+#include <stdarg.h>
 #include <stdio.h>
 #include <stdint.h>
 #include <stdbool.h>
Daniel Borkmann March 12, 2019, 11:20 a.m. UTC | #4
On 03/11/2019 09:57 PM, Arnaldo Carvalho de Melo wrote:
> Em Mon, Mar 11, 2019 at 09:45:49PM +0100, Daniel Borkmann escreveu:
>> On 03/11/2019 07:26 PM, Arnaldo Carvalho de Melo wrote:
>>> Em Thu, Mar 07, 2019 at 09:57:57AM -0800, Song Liu escreveu:
>>>> Currently, bpf_prog_info includes 9 arrays. The user has the option to
>>>> fetch any combination of these arrays. However, this requires a lot of
>>>> handling of these arrays. This work becomes more tricky when we need to
>>>> store bpf_prog_info to a file, because these arrays are allocated
>>>> independently.
>>>>
>>>> This patch introduces struct bpf_prog_info_linear, which stores arrays
>>>> of bpf_prog_info in continues memory. Helper functions are introduced
>>>> to unify the work to get different information of bpf_prog_info.
>>>> Specifically, bpf_program__get_prog_info_linear() allows the user to
>>>> select which arrays to fetch, and handles details for the user.
>>>>
>>>> Plesae see the comments before enum bpf_prog_info_array for more details
>>>> and examples.
>>>>
>>>> Cc: Daniel Borkmann <daniel@iogearbox.net>
>>>> Cc: Alexei Starovoitov <ast@kernel.org>
>>>
>>> Daniel, are you ok with these changes to libbpf and bpftool? Perhaps
>>> those should be detached from this patchkit and submitted sooner,
>>> eroding the size of this kit.
>>>
>>> Alternatively, if you're ok with it, please provide your Acked-by and
>>> I'll process as soon as I get back to it after Jiri is done reviewing.
>>
>> Overall looks okay. Are you planning to get these in for 5.1 window? If
> 
> I hope so.
> 
>> yes, that would be great, otherwise we might need to cherry-pick the libbpf
> 
> So I'll try and grab those for my next pull req to Ingo, this week,
> will add your Acked-by, ok?

Please do, thanks.

>> and bpftool ones from your tree into bpf-next as well since there's just
>> too much going on in this area where we'd potentially run into complex
>> merge conflicts. In the latter case, libbpf.map would need to be fixed up
>> to LIBBPF_0.0.3 as convention is that this is in line with kernel release.
> 
> BTW, while running my build tests I had to add this to today's batch,
> should be trivial when merging, I think, ok?

That's fine as well, ack, lets shoot for getting it in during merge window
such that we don't need to duplicate these dependencies in bpf trees.

> commit dfcbc2f2994b8a3af3605a26dc29c07ad7378bf4
> Author: Arnaldo Carvalho de Melo <acme@redhat.com>
> Date:   Mon Mar 11 17:07:52 2019 -0300
> 
>     tools lib bpf: Fix the build by adding a missing stdarg.h include
>     
>     The libbpf_print_fn_t typedef uses va_list without including the header
>     where that type is defined, stdarg.h, breaking in places where we're
>     unlucky for that type not to be already defined by some previously
>     included header.
>     
>     Noticed while building on fedora 24 cross building tools/perf to the ARC
>     architecture using the uClibc C library:
>     
>       28 fedora:24-x-ARC-uClibc   : FAIL arc-linux-gcc (ARCompact ISA Linux uClibc toolchain 2017.09-rc2) 7.1.1 20170710
>     
>         CC       /tmp/build/perf/tests/llvm.o
>       In file included from tests/llvm.c:3:0:
>       /git/linux/tools/lib/bpf/libbpf.h:57:20: error: unknown type name 'va_list'
>             const char *, va_list ap);
>                           ^~~~~~~
>       /git/linux/tools/lib/bpf/libbpf.h:59:34: error: unknown type name 'libbpf_print_fn_t'
>        LIBBPF_API void libbpf_set_print(libbpf_print_fn_t fn);
>                                         ^~~~~~~~~~~~~~~~~
>       mv: cannot stat '/tmp/build/perf/tests/.llvm.o.tmp': No such file or directory
>     
>     Cc: Alexei Starovoitov <ast@kernel.org>
>     Cc: Daniel Borkmann <daniel@iogearbox.net>
>     Cc: Jakub Kicinski <jakub.kicinski@netronome.com>
>     Cc: Jiri Olsa <jolsa@kernel.org>
>     Cc: Namhyung Kim <namhyung@kernel.org>
>     Cc: Quentin Monnet <quentin.monnet@netronome.com>
>     Cc: Stanislav Fomichev <sdf@google.com>
>     Cc: Yonghong Song <yhs@fb.com>
>     Fixes: a8a1f7d09cfc ("libbpf: fix libbpf_print")
>     Link: https://lkml.kernel.org/n/tip-5270n2quu2gqz22o7itfdx00@git.kernel.org
>     Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
> 
> diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
> index b4652aa1a58a..aa1521a51687 100644
> --- a/tools/lib/bpf/libbpf.h
> +++ b/tools/lib/bpf/libbpf.h
> @@ -10,6 +10,7 @@
>  #ifndef __LIBBPF_LIBBPF_H
>  #define __LIBBPF_LIBBPF_H
>  
> +#include <stdarg.h>
>  #include <stdio.h>
>  #include <stdint.h>
>  #include <stdbool.h>
> 

Thanks,
Daniel
diff mbox series

Patch

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index f5eb60379c8d..ca00ce5cbae0 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -112,6 +112,11 @@  void libbpf_print(enum libbpf_print_level level, const char *format, ...)
 # define LIBBPF_ELF_C_READ_MMAP ELF_C_READ
 #endif
 
+static inline __u64 ptr_to_u64(const void *ptr)
+{
+	return (__u64) (unsigned long) ptr;
+}
+
 struct bpf_capabilities {
 	/* v4.14: kernel support for program & map names. */
 	__u32 name:1;
@@ -2997,3 +3002,249 @@  bpf_perf_event_read_simple(void *mmap_mem, size_t mmap_size, size_t page_size,
 	ring_buffer_write_tail(header, data_tail);
 	return ret;
 }
+
+struct bpf_prog_info_array_desc {
+	int	array_offset;	/* e.g. offset of jited_prog_insns */
+	int	count_offset;	/* e.g. offset of jited_prog_len */
+	int	size_offset;	/* > 0: offset of rec size,
+				 * < 0: fix size of -size_offset
+				 */
+};
+
+static struct bpf_prog_info_array_desc bpf_prog_info_array_desc[] = {
+	[BPF_PROG_INFO_JITED_INSNS] = {
+		offsetof(struct bpf_prog_info, jited_prog_insns),
+		offsetof(struct bpf_prog_info, jited_prog_len),
+		-1,
+	},
+	[BPF_PROG_INFO_XLATED_INSNS] = {
+		offsetof(struct bpf_prog_info, xlated_prog_insns),
+		offsetof(struct bpf_prog_info, xlated_prog_len),
+		-1,
+	},
+	[BPF_PROG_INFO_MAP_IDS] = {
+		offsetof(struct bpf_prog_info, map_ids),
+		offsetof(struct bpf_prog_info, nr_map_ids),
+		-(int)sizeof(__u32),
+	},
+	[BPF_PROG_INFO_JITED_KSYMS] = {
+		offsetof(struct bpf_prog_info, jited_ksyms),
+		offsetof(struct bpf_prog_info, nr_jited_ksyms),
+		-(int)sizeof(__u64),
+	},
+	[BPF_PROG_INFO_JITED_FUNC_LENS] = {
+		offsetof(struct bpf_prog_info, jited_func_lens),
+		offsetof(struct bpf_prog_info, nr_jited_func_lens),
+		-(int)sizeof(__u32),
+	},
+	[BPF_PROG_INFO_FUNC_INFO] = {
+		offsetof(struct bpf_prog_info, func_info),
+		offsetof(struct bpf_prog_info, nr_func_info),
+		offsetof(struct bpf_prog_info, func_info_rec_size),
+	},
+	[BPF_PROG_INFO_LINE_INFO] = {
+		offsetof(struct bpf_prog_info, line_info),
+		offsetof(struct bpf_prog_info, nr_line_info),
+		offsetof(struct bpf_prog_info, line_info_rec_size),
+	},
+	[BPF_PROG_INFO_JITED_LINE_INFO] = {
+		offsetof(struct bpf_prog_info, jited_line_info),
+		offsetof(struct bpf_prog_info, nr_jited_line_info),
+		offsetof(struct bpf_prog_info, jited_line_info_rec_size),
+	},
+	[BPF_PROG_INFO_PROG_TAGS] = {
+		offsetof(struct bpf_prog_info, prog_tags),
+		offsetof(struct bpf_prog_info, nr_prog_tags),
+		-(int)sizeof(__u8) * BPF_TAG_SIZE,
+	},
+
+};
+
+static __u32 bpf_prog_info_read_offset_u32(struct bpf_prog_info *info, int offset)
+{
+	__u32 *array = (__u32 *)info;
+
+	if (offset >= 0)
+		return array[offset / sizeof(__u32)];
+	return -(int)offset;
+}
+
+static __u64 bpf_prog_info_read_offset_u64(struct bpf_prog_info *info, int offset)
+{
+	__u64 *array = (__u64 *)info;
+
+	if (offset >= 0)
+		return array[offset / sizeof(__u64)];
+	return -(int)offset;
+}
+
+static void bpf_prog_info_set_offset_u32(struct bpf_prog_info *info, int offset,
+					 __u32 val)
+{
+	__u32 *array = (__u32 *)info;
+
+	if (offset >= 0)
+		array[offset / sizeof(__u32)] = val;
+}
+
+static void bpf_prog_info_set_offset_u64(struct bpf_prog_info *info, int offset,
+					 __u64 val)
+{
+	__u64 *array = (__u64 *)info;
+
+	if (offset >= 0)
+		array[offset / sizeof(__u64)] = val;
+}
+
+struct bpf_prog_info_linear *
+bpf_program__get_prog_info_linear(int fd, __u64 arrays)
+{
+	struct bpf_prog_info_linear *info_linear;
+	struct bpf_prog_info info = {};
+	__u32 info_len = sizeof(info);
+	__u32 data_len = 0;
+	int i, err;
+	void *ptr;
+
+	if (arrays >> BPF_PROG_INFO_LAST_ARRAY)
+		return ERR_PTR(-EINVAL);
+
+	/* step 1: get array dimensions */
+	err = bpf_obj_get_info_by_fd(fd, &info, &info_len);
+	if (err) {
+		pr_debug("can't get prog info: %s", strerror(errno));
+		return ERR_PTR(-EFAULT);
+	}
+
+	/* step 2: calculate total size of all arrays */
+	for (i = BPF_PROG_INFO_FIRST_ARRAY; i < BPF_PROG_INFO_LAST_ARRAY; ++i) {
+		bool include_array = (arrays & (1UL << i)) > 0;
+		struct bpf_prog_info_array_desc *desc;
+		__u32 count, size;
+
+		desc = bpf_prog_info_array_desc + i;
+
+		/* kernel is too old to support this field */
+		if (info_len < desc->array_offset + sizeof(__u32) ||
+		    info_len < desc->count_offset + sizeof(__u32) ||
+		    (desc->size_offset > 0 && info_len < desc->size_offset))
+			include_array = false;
+
+		if (!include_array) {
+			arrays &= ~(1UL << i);	/* clear the bit */
+			continue;
+		}
+
+		count = bpf_prog_info_read_offset_u32(&info, desc->count_offset);
+		size = bpf_prog_info_read_offset_u32(&info, desc->size_offset);
+
+		data_len += count * size;
+	}
+
+	/* step 3: allocate continuous memory */
+	data_len = roundup(data_len, sizeof(__u64));
+	info_linear = malloc(sizeof(struct bpf_prog_info_linear) + data_len);
+	if (!info_linear)
+		return ERR_PTR(-ENOMEM);
+
+	/* step 4: fill data to info_linear->info */
+	info_linear->arrays = arrays;
+	memset(&info_linear->info, 0, sizeof(info));
+	ptr = info_linear->data;
+
+	for (i = BPF_PROG_INFO_FIRST_ARRAY; i < BPF_PROG_INFO_LAST_ARRAY; ++i) {
+		struct bpf_prog_info_array_desc *desc;
+		__u32 count, size;
+
+		if ((arrays & (1UL << i)) == 0)
+			continue;
+
+		desc = bpf_prog_info_array_desc + i;
+		count = bpf_prog_info_read_offset_u32(&info, desc->count_offset);
+		size = bpf_prog_info_read_offset_u32(&info, desc->size_offset);
+		bpf_prog_info_set_offset_u32(&info_linear->info,
+					     desc->count_offset, count);
+		bpf_prog_info_set_offset_u32(&info_linear->info,
+					     desc->size_offset, size);
+		bpf_prog_info_set_offset_u64(&info_linear->info,
+					     desc->array_offset,
+					     ptr_to_u64(ptr));
+		ptr += count * size;
+	}
+
+	/* step 5: call syscall again to get required arrays */
+	err = bpf_obj_get_info_by_fd(fd, &info_linear->info, &info_len);
+	if (err) {
+		pr_debug("can't get prog info: %s", strerror(errno));
+		free(info_linear);
+		return ERR_PTR(-EFAULT);
+	}
+
+	/* step 6: verify the data */
+	for (i = BPF_PROG_INFO_FIRST_ARRAY; i < BPF_PROG_INFO_LAST_ARRAY; ++i) {
+		struct bpf_prog_info_array_desc *desc;
+		__u32 v1, v2;
+
+		if ((arrays & (1UL << i)) == 0)
+			continue;
+
+		desc = bpf_prog_info_array_desc + i;
+		v1 = bpf_prog_info_read_offset_u32(&info, desc->count_offset);
+		v2 = bpf_prog_info_read_offset_u32(&info_linear->info,
+						   desc->count_offset);
+		if (v1 != v2)
+			pr_warning("%s: mismatch in element count\n", __func__);
+
+		v1 = bpf_prog_info_read_offset_u32(&info, desc->size_offset);
+		v2 = bpf_prog_info_read_offset_u32(&info_linear->info,
+						   desc->size_offset);
+		if (v1 != v2)
+			pr_warning("%s: mismatch in rec size\n", __func__);
+	}
+
+	/* step 7: update info_len and data_len */
+	info_linear->info_len = sizeof(struct bpf_prog_info);
+	info_linear->data_len = data_len;
+
+	return info_linear;
+}
+
+void bpf_program__bpil_addr_to_offs(struct bpf_prog_info_linear *info_linear)
+{
+	int i;
+
+	for (i = BPF_PROG_INFO_FIRST_ARRAY; i < BPF_PROG_INFO_LAST_ARRAY; ++i) {
+		struct bpf_prog_info_array_desc *desc;
+		__u64 addr, offs;
+
+		if ((info_linear->arrays & (1UL << i)) == 0)
+			continue;
+
+		desc = bpf_prog_info_array_desc + i;
+		addr = bpf_prog_info_read_offset_u64(&info_linear->info,
+						     desc->array_offset);
+		offs = addr - ptr_to_u64(info_linear->data);
+		bpf_prog_info_set_offset_u64(&info_linear->info,
+					     desc->array_offset, offs);
+	}
+}
+
+void bpf_program__bpil_offs_to_addr(struct bpf_prog_info_linear *info_linear)
+{
+	int i;
+
+	for (i = BPF_PROG_INFO_FIRST_ARRAY; i < BPF_PROG_INFO_LAST_ARRAY; ++i) {
+		struct bpf_prog_info_array_desc *desc;
+		__u64 addr, offs;
+
+		if ((info_linear->arrays & (1UL << i)) == 0)
+			continue;
+
+		desc = bpf_prog_info_array_desc + i;
+		offs = bpf_prog_info_read_offset_u64(&info_linear->info,
+						     desc->array_offset);
+		addr = offs + ptr_to_u64(info_linear->data);
+		bpf_prog_info_set_offset_u64(&info_linear->info,
+					     desc->array_offset, addr);
+	}
+}
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index b4652aa1a58a..c7645a5e1ac0 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -377,6 +377,69 @@  LIBBPF_API bool bpf_probe_map_type(enum bpf_map_type map_type, __u32 ifindex);
 LIBBPF_API bool bpf_probe_helper(enum bpf_func_id id,
 				 enum bpf_prog_type prog_type, __u32 ifindex);
 
+/*
+ * Get bpf_prog_info in continuous memory
+ *
+ * struct bpf_prog_info has multiple arrays. The user has option to choose
+ * arrays to fetch from kernel. The following APIs provide uniform way to
+ * fetch these data. All arrays in bpf_prog_info are stored in singile
+ * continuous memory region. This makes it easy to store the info in a
+ * file.
+ *
+ * Before writing bpf_prog_info_linear to files, it is necessary to
+ * translate pointers bpf_prog_info to offsets. Helper functions
+ * bpf_program__bpil_addr_to_offs() and bpf_program__bpil_offs_to_addr()
+ * are introduced to switch between pointers and offsets.
+ *
+ * Examples:
+ *   # To fetch map_ids and prog_tags:
+ *   __u64 arrays = (1UL << BPF_PROG_INFO_MAP_IDS) |
+ *           (1UL << BPF_PROG_INFO_PROG_TAGS);
+ *   struct bpf_prog_info_linear *info_linear =
+ *           bpf_program__get_prog_info_linear(fd, arrays);
+ *
+ *   # To save data in file
+ *   bpf_program__bpil_addr_to_offs(info_linear);
+ *   write(f, info_linear, sizeof(*info_linear) + info_linear->data_len);
+ *
+ *   # To read data from file
+ *   read(f, info_linear, <proper_size>);
+ *   bpf_program__bpil_offs_to_addr(info_linear);
+ */
+enum bpf_prog_info_array {
+	BPF_PROG_INFO_FIRST_ARRAY = 0,
+	BPF_PROG_INFO_JITED_INSNS = 0,
+	BPF_PROG_INFO_XLATED_INSNS,
+	BPF_PROG_INFO_MAP_IDS,
+	BPF_PROG_INFO_JITED_KSYMS,
+	BPF_PROG_INFO_JITED_FUNC_LENS,
+	BPF_PROG_INFO_FUNC_INFO,
+	BPF_PROG_INFO_LINE_INFO,
+	BPF_PROG_INFO_JITED_LINE_INFO,
+	BPF_PROG_INFO_PROG_TAGS,
+	BPF_PROG_INFO_LAST_ARRAY,
+};
+
+struct bpf_prog_info_linear {
+	/* size of struct bpf_prog_info, when the tool is compiled */
+	__u32			info_len;
+	/* total bytes allocated for data, round up to 8 bytes */
+	__u32			data_len;
+	/* which arrays are included in data */
+	__u64			arrays;
+	struct bpf_prog_info	info;
+	__u8			data[];
+};
+
+LIBBPF_API struct bpf_prog_info_linear *
+bpf_program__get_prog_info_linear(int fd, __u64 arrays);
+
+LIBBPF_API void
+bpf_program__bpil_addr_to_offs(struct bpf_prog_info_linear *info_linear);
+
+LIBBPF_API void
+bpf_program__bpil_offs_to_addr(struct bpf_prog_info_linear *info_linear);
+
 #ifdef __cplusplus
 } /* extern "C" */
 #endif
diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
index 778a26702a70..f3ce50500cf2 100644
--- a/tools/lib/bpf/libbpf.map
+++ b/tools/lib/bpf/libbpf.map
@@ -153,4 +153,7 @@  LIBBPF_0.0.2 {
 		xsk_socket__delete;
 		xsk_umem__fd;
 		xsk_socket__fd;
+		bpf_program__get_prog_info_linear;
+		bpf_program__bpil_addr_to_offs;
+		bpf_program__bpil_offs_to_addr;
 } LIBBPF_0.0.1;