Commit bd6b3b35 authored by Alexei Starovoitov's avatar Alexei Starovoitov Committed by Andrii Nakryiko
Browse files

Merge branch 'Enhance and rework logging controls in libbpf'



Andrii Nakryiko says:

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

Add new open options and per-program setters to control BTF and program
loading log verboseness and allow providing custom log buffers to capture logs
of interest. Note how custom log_buf and log_level are orthogonal, which
matches previous (alas less customizable) behavior of libbpf, even though it
sort of worked by accident: if someone specified log_level = 1 in
bpf_object__load_xattr(), first attempt to load any BPF program resulted in
wasted bpf() syscall with -EINVAL due to !!log_buf != !!log_level. Then on
retry libbpf would allocated log_buffer and try again, after which prog
loading would succeed and libbpf would print verbose program loading log
through its print callback.

This behavior is now documented and made more efficient, not wasting
unnecessary syscall. But additionally, log_level can be controlled globally on
a per-bpf_object level through bpf_object_open_opts, as well as on
a per-program basis with bpf_program__set_log_buf() and
bpf_program__set_log_level() APIs.

Now that we have a more future-proof way to set log_level, deprecate
bpf_object__load_xattr().

v2->v3:
  - added log_buf selftests for bpf_prog_load() and bpf_btf_load();
  - fix !log_buf in bpf_prog_load (John);
  - fix log_level==0 in bpf_btf_load (thanks selftest!);

v1->v2:
  - fix log_level == 0 handling of bpf_prog_load, add as patch #1 (Alexei);
  - add comments explaining log_buf_size overflow prevention (Alexei).
====================

Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
Signed-off-by: default avatarAndrii Nakryiko <andrii@kernel.org>
parents ac55b3f0 b59e4ce8
Loading
Loading
Loading
Loading
+4 −7
Original line number Diff line number Diff line
@@ -486,7 +486,6 @@ static void codegen_destroy(struct bpf_object *obj, const char *obj_name)

static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *header_guard)
{
	struct bpf_object_load_attr load_attr = {};
	DECLARE_LIBBPF_OPTS(gen_loader_opts, opts);
	struct bpf_map *map;
	char ident[256];
@@ -496,12 +495,7 @@ static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *h
	if (err)
		return err;

	load_attr.obj = obj;
	if (verifier_logs)
		/* log_level1 + log_level2 + stats, but not stable UAPI */
		load_attr.log_level = 1 + 2 + 4;

	err = bpf_object__load_xattr(&load_attr);
	err = bpf_object__load(obj);
	if (err) {
		p_err("failed to load object file");
		goto out;
@@ -719,6 +713,9 @@ static int do_skeleton(int argc, char **argv)
	if (obj_name[0] == '\0')
		get_obj_name(obj_name, file);
	opts.object_name = obj_name;
	if (verifier_logs)
		/* log_level1 + log_level2 + stats, but not stable UAPI */
		opts.kernel_log_level = 1 + 2 + 4;
	obj = bpf_object__open_mem(obj_data, file_sz, &opts);
	err = libbpf_get_error(obj);
	if (err) {
+10 −14
Original line number Diff line number Diff line
@@ -1464,7 +1464,6 @@ static int load_with_options(int argc, char **argv, bool first_prog_only)
	DECLARE_LIBBPF_OPTS(bpf_object_open_opts, open_opts,
		.relaxed_maps = relaxed_maps,
	);
	struct bpf_object_load_attr load_attr = { 0 };
	enum bpf_attach_type expected_attach_type;
	struct map_replace *map_replace = NULL;
	struct bpf_program *prog = NULL, *pos;
@@ -1598,6 +1597,10 @@ static int load_with_options(int argc, char **argv, bool first_prog_only)

	set_max_rlimit();

	if (verifier_logs)
		/* log_level1 + log_level2 + stats, but not stable UAPI */
		open_opts.kernel_log_level = 1 + 2 + 4;

	obj = bpf_object__open_file(file, &open_opts);
	if (libbpf_get_error(obj)) {
		p_err("failed to open object file");
@@ -1677,12 +1680,7 @@ static int load_with_options(int argc, char **argv, bool first_prog_only)
		goto err_close_obj;
	}

	load_attr.obj = obj;
	if (verifier_logs)
		/* log_level1 + log_level2 + stats, but not stable UAPI */
		load_attr.log_level = 1 + 2 + 4;

	err = bpf_object__load_xattr(&load_attr);
	err = bpf_object__load(obj);
	if (err) {
		p_err("failed to load object file");
		goto err_close_obj;
@@ -1809,7 +1807,6 @@ static int do_loader(int argc, char **argv)
{
	DECLARE_LIBBPF_OPTS(bpf_object_open_opts, open_opts);
	DECLARE_LIBBPF_OPTS(gen_loader_opts, gen);
	struct bpf_object_load_attr load_attr = {};
	struct bpf_object *obj;
	const char *file;
	int err = 0;
@@ -1818,6 +1815,10 @@ static int do_loader(int argc, char **argv)
		return -1;
	file = GET_ARG();

	if (verifier_logs)
		/* log_level1 + log_level2 + stats, but not stable UAPI */
		open_opts.kernel_log_level = 1 + 2 + 4;

	obj = bpf_object__open_file(file, &open_opts);
	if (libbpf_get_error(obj)) {
		p_err("failed to open object file");
@@ -1828,12 +1829,7 @@ static int do_loader(int argc, char **argv)
	if (err)
		goto err_close_obj;

	load_attr.obj = obj;
	if (verifier_logs)
		/* log_level1 + log_level2 + stats, but not stable UAPI */
		load_attr.log_level = 1 + 2 + 4;

	err = bpf_object__load_xattr(&load_attr);
	err = bpf_object__load(obj);
	if (err) {
		p_err("failed to load object file");
		goto err_close_obj;
+7 −8
Original line number Diff line number Diff line
@@ -479,7 +479,7 @@ static int do_unregister(int argc, char **argv)

static int do_register(int argc, char **argv)
{
	struct bpf_object_load_attr load_attr = {};
	LIBBPF_OPTS(bpf_object_open_opts, open_opts);
	const struct bpf_map_def *def;
	struct bpf_map_info info = {};
	__u32 info_len = sizeof(info);
@@ -494,18 +494,17 @@ static int do_register(int argc, char **argv)

	file = GET_ARG();

	obj = bpf_object__open(file);
	if (verifier_logs)
		/* log_level1 + log_level2 + stats, but not stable UAPI */
		open_opts.kernel_log_level = 1 + 2 + 4;

	obj = bpf_object__open_file(file, &open_opts);
	if (libbpf_get_error(obj))
		return -1;

	set_max_rlimit();

	load_attr.obj = obj;
	if (verifier_logs)
		/* log_level1 + log_level2 + stats, but not stable UAPI */
		load_attr.log_level = 1 + 2 + 4;

	if (bpf_object__load_xattr(&load_attr)) {
	if (bpf_object__load(obj)) {
		bpf_object__close(obj);
		return -1;
	}
+66 −22
Original line number Diff line number Diff line
@@ -303,10 +303,6 @@ int bpf_prog_load_v0_6_0(enum bpf_prog_type prog_type,
	if (log_level && !log_buf)
		return libbpf_err(-EINVAL);

	attr.log_level = log_level;
	attr.log_buf = ptr_to_u64(log_buf);
	attr.log_size = log_size;

	func_info_rec_size = OPTS_GET(opts, func_info_rec_size, 0);
	func_info = OPTS_GET(opts, func_info, NULL);
	attr.func_info_rec_size = func_info_rec_size;
@@ -321,6 +317,12 @@ int bpf_prog_load_v0_6_0(enum bpf_prog_type prog_type,

	attr.fd_array = ptr_to_u64(OPTS_GET(opts, fd_array, NULL));

	if (log_level) {
		attr.log_buf = ptr_to_u64(log_buf);
		attr.log_size = log_size;
		attr.log_level = log_level;
	}

	fd = sys_bpf_prog_load(&attr, sizeof(attr), attempts);
	if (fd >= 0)
		return fd;
@@ -366,16 +368,17 @@ int bpf_prog_load_v0_6_0(enum bpf_prog_type prog_type,
			goto done;
	}

	if (log_level || !log_buf)
		goto done;

	/* Try again with log */
	log_buf[0] = 0;
	if (log_level == 0 && log_buf) {
		/* log_level == 0 with non-NULL log_buf requires retrying on error
		 * with log_level == 1 and log_buf/log_buf_size set, to get details of
		 * failure
		 */
		attr.log_buf = ptr_to_u64(log_buf);
		attr.log_size = log_size;
		attr.log_level = 1;

		fd = sys_bpf_prog_load(&attr, sizeof(attr), attempts);
	}
done:
	/* free() doesn't affect errno, so we don't need to restore it */
	free(finfo);
@@ -1044,24 +1047,65 @@ int bpf_raw_tracepoint_open(const char *name, int prog_fd)
	return libbpf_err_errno(fd);
}

int bpf_load_btf(const void *btf, __u32 btf_size, char *log_buf, __u32 log_buf_size,
		 bool do_log)
int bpf_btf_load(const void *btf_data, size_t btf_size, const struct bpf_btf_load_opts *opts)
{
	union bpf_attr attr = {};
	const size_t attr_sz = offsetofend(union bpf_attr, btf_log_level);
	union bpf_attr attr;
	char *log_buf;
	size_t log_size;
	__u32 log_level;
	int fd;

	attr.btf = ptr_to_u64(btf);
	memset(&attr, 0, attr_sz);

	if (!OPTS_VALID(opts, bpf_btf_load_opts))
		return libbpf_err(-EINVAL);

	log_buf = OPTS_GET(opts, log_buf, NULL);
	log_size = OPTS_GET(opts, log_size, 0);
	log_level = OPTS_GET(opts, log_level, 0);

	if (log_size > UINT_MAX)
		return libbpf_err(-EINVAL);
	if (log_size && !log_buf)
		return libbpf_err(-EINVAL);

	attr.btf = ptr_to_u64(btf_data);
	attr.btf_size = btf_size;
	/* log_level == 0 and log_buf != NULL means "try loading without
	 * log_buf, but retry with log_buf and log_level=1 on error", which is
	 * consistent across low-level and high-level BTF and program loading
	 * APIs within libbpf and provides a sensible behavior in practice
	 */
	if (log_level) {
		attr.btf_log_buf = ptr_to_u64(log_buf);
		attr.btf_log_size = (__u32)log_size;
		attr.btf_log_level = log_level;
	}

retry:
	if (do_log && log_buf && log_buf_size) {
		attr.btf_log_level = 1;
		attr.btf_log_size = log_buf_size;
	fd = sys_bpf_fd(BPF_BTF_LOAD, &attr, attr_sz);
	if (fd < 0 && log_buf && log_level == 0) {
		attr.btf_log_buf = ptr_to_u64(log_buf);
		attr.btf_log_size = (__u32)log_size;
		attr.btf_log_level = 1;
		fd = sys_bpf_fd(BPF_BTF_LOAD, &attr, attr_sz);
	}
	return libbpf_err_errno(fd);
}

int bpf_load_btf(const void *btf, __u32 btf_size, char *log_buf, __u32 log_buf_size, bool do_log)
{
	LIBBPF_OPTS(bpf_btf_load_opts, opts);
	int fd;

	fd = sys_bpf_fd(BPF_BTF_LOAD, &attr, sizeof(attr));
retry:
	if (do_log && log_buf && log_buf_size) {
		opts.log_buf = log_buf;
		opts.log_size = log_buf_size;
		opts.log_level = 1;
	}

	fd = bpf_btf_load(btf, btf_size, &opts);
	if (fd < 0 && !do_log && log_buf && log_buf_size) {
		do_log = true;
		goto retry;
+19 −3
Original line number Diff line number Diff line
@@ -195,8 +195,9 @@ struct bpf_load_program_attr {
/* Flags to direct loading requirements */
#define MAPS_RELAX_COMPAT	0x01

/* Recommend log buffer size */
/* Recommended log buffer size */
#define BPF_LOG_BUF_SIZE (UINT32_MAX >> 8) /* verifier maximum in kernels <= 5.1 */

LIBBPF_DEPRECATED_SINCE(0, 7, "use bpf_prog_load() instead")
LIBBPF_API int bpf_load_program_xattr(const struct bpf_load_program_attr *load_attr,
				      char *log_buf, size_t log_buf_sz);
@@ -213,6 +214,23 @@ LIBBPF_API int bpf_verify_program(enum bpf_prog_type type,
				  char *log_buf, size_t log_buf_sz,
				  int log_level);

struct bpf_btf_load_opts {
	size_t sz; /* size of this struct for forward/backward compatibility */

	/* kernel log options */
	char *log_buf;
	__u32 log_level;
	__u32 log_size;
};
#define bpf_btf_load_opts__last_field log_size

LIBBPF_API int bpf_btf_load(const void *btf_data, size_t btf_size,
			    const struct bpf_btf_load_opts *opts);

LIBBPF_DEPRECATED_SINCE(0, 8, "use bpf_btf_load() instead")
LIBBPF_API int bpf_load_btf(const void *btf, __u32 btf_size, char *log_buf,
			    __u32 log_buf_size, bool do_log);

LIBBPF_API int bpf_map_update_elem(int fd, const void *key, const void *value,
				   __u64 flags);

@@ -340,8 +358,6 @@ LIBBPF_API int bpf_prog_query(int target_fd, enum bpf_attach_type type,
			      __u32 query_flags, __u32 *attach_flags,
			      __u32 *prog_ids, __u32 *prog_cnt);
LIBBPF_API int bpf_raw_tracepoint_open(const char *name, int prog_fd);
LIBBPF_API int bpf_load_btf(const void *btf, __u32 btf_size, char *log_buf,
			    __u32 log_buf_size, bool do_log);
LIBBPF_API int bpf_task_fd_query(int pid, int fd, __u32 flags, char *buf,
				 __u32 *buf_len, __u32 *prog_id, __u32 *fd_type,
				 __u64 *probe_offset, __u64 *probe_addr);
Loading