Commit 1a190d1e authored by Andrii Nakryiko's avatar Andrii Nakryiko

libbpf: Allow passing preallocated log_buf when loading BTF into kernel

Add libbpf-internal btf_load_into_kernel() that allows to pass
preallocated log_buf and custom log_level to be passed into kernel
during BPF_BTF_LOAD call. When custom log_buf is provided,
btf_load_into_kernel() won't attempt an retry with automatically
allocated internal temporary buffer to capture BTF validation log.

It's important to note the relation between log_buf and log_level, which
slightly deviates from stricter kernel logic. From kernel's POV, if
log_buf is specified, log_level has to be > 0, and vice versa. While
kernel has good reasons to request such "sanity, this, in practice, is
a bit unconvenient and restrictive for libbpf's high-level bpf_object APIs.

So libbpf will allow to set non-NULL log_buf and log_level == 0. This is
fine and means to attempt to load BTF without logging requested, but if
it failes, retry the load with custom log_buf and log_level 1. Similar
logic will be implemented for program loading. In practice this means
that users can provide custom log buffer just in case error happens, but
not really request slower verbose logging all the time. This is also
consistent with libbpf behavior when custom log_buf is not set: libbpf
first tries to load everything with log_level=0, and only if error
happens allocates internal log buffer and retries with log_level=1.

Also, while at it, make BTF validation log more obvious and follow the log
pattern libbpf is using for dumping BPF verifier log during
BPF_PROG_LOAD. BTF loading resulting in an error will look like this:

libbpf: BTF loading error: -22
libbpf: -- BEGIN BTF LOAD LOG ---
magic: 0xeb9f
version: 1
flags: 0x0
hdr_len: 24
type_off: 0
type_len: 1040
str_off: 1040
str_len: 2063598257
btf_total_size: 1753
Total section length too long
-- END BTF LOAD LOG --
libbpf: Error loading .BTF into kernel: -22. BTF is optional, ignoring.

This makes it much easier to find relevant parts in libbpf log output.
Signed-off-by: default avatarAndrii Nakryiko <andrii@kernel.org>
Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
Link: https://lore.kernel.org/bpf/20211209193840.1248570-4-andrii@kernel.org
parent 0ed08d67
...@@ -1124,54 +1124,86 @@ struct btf *btf__parse_split(const char *path, struct btf *base_btf) ...@@ -1124,54 +1124,86 @@ struct btf *btf__parse_split(const char *path, struct btf *base_btf)
static void *btf_get_raw_data(const struct btf *btf, __u32 *size, bool swap_endian); static void *btf_get_raw_data(const struct btf *btf, __u32 *size, bool swap_endian);
int btf__load_into_kernel(struct btf *btf) int btf_load_into_kernel(struct btf *btf, char *log_buf, size_t log_sz, __u32 log_level)
{ {
__u32 log_buf_size = 0, raw_size; LIBBPF_OPTS(bpf_btf_load_opts, opts);
char *log_buf = NULL; __u32 buf_sz = 0, raw_size;
char *buf = NULL, *tmp;
void *raw_data; void *raw_data;
int err = 0; int err = 0;
if (btf->fd >= 0) if (btf->fd >= 0)
return libbpf_err(-EEXIST); return libbpf_err(-EEXIST);
if (log_sz && !log_buf)
return libbpf_err(-EINVAL);
retry_load: /* cache native raw data representation */
if (log_buf_size) {
log_buf = malloc(log_buf_size);
if (!log_buf)
return libbpf_err(-ENOMEM);
*log_buf = 0;
}
raw_data = btf_get_raw_data(btf, &raw_size, false); raw_data = btf_get_raw_data(btf, &raw_size, false);
if (!raw_data) { if (!raw_data) {
err = -ENOMEM; err = -ENOMEM;
goto done; goto done;
} }
/* cache native raw data representation */
btf->raw_size = raw_size; btf->raw_size = raw_size;
btf->raw_data = raw_data; btf->raw_data = raw_data;
btf->fd = bpf_load_btf(raw_data, raw_size, log_buf, log_buf_size, false); retry_load:
/* if log_level is 0, we won't provide log_buf/log_size to the kernel,
* initially. Only if BTF loading fails, we bump log_level to 1 and
* retry, using either auto-allocated or custom log_buf. This way
* non-NULL custom log_buf provides a buffer just in case, but hopes
* for successful load and no need for log_buf.
*/
if (log_level) {
/* if caller didn't provide custom log_buf, we'll keep
* allocating our own progressively bigger buffers for BTF
* verification log
*/
if (!log_buf) {
buf_sz = max((__u32)BPF_LOG_BUF_SIZE, buf_sz * 2);
tmp = realloc(buf, buf_sz);
if (!tmp) {
err = -ENOMEM;
goto done;
}
buf = tmp;
buf[0] = '\0';
}
opts.log_buf = log_buf ? log_buf : buf;
opts.log_size = log_buf ? log_sz : buf_sz;
opts.log_level = log_level;
}
btf->fd = bpf_btf_load(raw_data, raw_size, &opts);
if (btf->fd < 0) { if (btf->fd < 0) {
if (!log_buf || errno == ENOSPC) { /* time to turn on verbose mode and try again */
log_buf_size = max((__u32)BPF_LOG_BUF_SIZE, if (log_level == 0) {
log_buf_size << 1); log_level = 1;
free(log_buf);
goto retry_load; goto retry_load;
} }
/* only retry if caller didn't provide custom log_buf, but
* make sure we can never overflow buf_sz
*/
if (!log_buf && errno == ENOSPC && buf_sz <= UINT_MAX / 2)
goto retry_load;
err = -errno; err = -errno;
pr_warn("Error loading BTF: %s(%d)\n", strerror(errno), errno); pr_warn("BTF loading error: %d\n", err);
if (*log_buf) /* don't print out contents of custom log_buf */
pr_warn("%s\n", log_buf); if (!log_buf && buf[0])
goto done; pr_warn("-- BEGIN BTF LOAD LOG ---\n%s\n-- END BTF LOAD LOG --\n", buf);
} }
done: done:
free(log_buf); free(buf);
return libbpf_err(err); return libbpf_err(err);
} }
int btf__load_into_kernel(struct btf *btf)
{
return btf_load_into_kernel(btf, NULL, 0, 0);
}
int btf__load(struct btf *) __attribute__((alias("btf__load_into_kernel"))); int btf__load(struct btf *) __attribute__((alias("btf__load_into_kernel")));
int btf__fd(const struct btf *btf) int btf__fd(const struct btf *btf)
......
...@@ -277,6 +277,7 @@ int parse_cpu_mask_str(const char *s, bool **mask, int *mask_sz); ...@@ -277,6 +277,7 @@ int parse_cpu_mask_str(const char *s, bool **mask, int *mask_sz);
int parse_cpu_mask_file(const char *fcpu, bool **mask, int *mask_sz); int parse_cpu_mask_file(const char *fcpu, bool **mask, int *mask_sz);
int libbpf__load_raw_btf(const char *raw_types, size_t types_len, int libbpf__load_raw_btf(const char *raw_types, size_t types_len,
const char *str_sec, size_t str_len); const char *str_sec, size_t str_len);
int btf_load_into_kernel(struct btf *btf, char *log_buf, size_t log_sz, __u32 log_level);
struct btf *btf_get_from_fd(int btf_fd, struct btf *base_btf); struct btf *btf_get_from_fd(int btf_fd, struct btf *base_btf);
void btf_get_kernel_prefix_kind(enum bpf_attach_type attach_type, void btf_get_kernel_prefix_kind(enum bpf_attach_type attach_type,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment