Commit 8d1608d7 authored by Kui-Feng Lee's avatar Kui-Feng Lee Committed by Martin KaFai Lau
Browse files

libbpf: Create a bpf_link in bpf_map__attach_struct_ops().



bpf_map__attach_struct_ops() was creating a dummy bpf_link as a
placeholder, but now it is constructing an authentic one by calling
bpf_link_create() if the map has the BPF_F_LINK flag.

You can flag a struct_ops map with BPF_F_LINK by calling
bpf_map__set_map_flags().

Signed-off-by: default avatarKui-Feng Lee <kuifeng@meta.com>
Acked-by: default avatarAndrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/r/20230323032405.3735486-5-kuifeng@meta.com


Signed-off-by: default avatarMartin KaFai Lau <martin.lau@kernel.org>
parent 68b04864
Loading
Loading
Loading
Loading
+71 −24
Original line number Diff line number Diff line
@@ -116,6 +116,7 @@ static const char * const attach_type_name[] = {
	[BPF_SK_REUSEPORT_SELECT_OR_MIGRATE]	= "sk_reuseport_select_or_migrate",
	[BPF_PERF_EVENT]		= "perf_event",
	[BPF_TRACE_KPROBE_MULTI]	= "trace_kprobe_multi",
	[BPF_STRUCT_OPS]		= "struct_ops",
};

static const char * const link_type_name[] = {
@@ -7686,6 +7687,37 @@ static int bpf_object__resolve_externs(struct bpf_object *obj,
	return 0;
}

static void bpf_map_prepare_vdata(const struct bpf_map *map)
{
	struct bpf_struct_ops *st_ops;
	__u32 i;

	st_ops = map->st_ops;
	for (i = 0; i < btf_vlen(st_ops->type); i++) {
		struct bpf_program *prog = st_ops->progs[i];
		void *kern_data;
		int prog_fd;

		if (!prog)
			continue;

		prog_fd = bpf_program__fd(prog);
		kern_data = st_ops->kern_vdata + st_ops->kern_func_off[i];
		*(unsigned long *)kern_data = prog_fd;
	}
}

static int bpf_object_prepare_struct_ops(struct bpf_object *obj)
{
	int i;

	for (i = 0; i < obj->nr_maps; i++)
		if (bpf_map__is_struct_ops(&obj->maps[i]))
			bpf_map_prepare_vdata(&obj->maps[i]);

	return 0;
}

static int bpf_object_load(struct bpf_object *obj, int extra_log_level, const char *target_btf_path)
{
	int err, i;
@@ -7711,6 +7743,7 @@ static int bpf_object_load(struct bpf_object *obj, int extra_log_level, const ch
	err = err ? : bpf_object__relocate(obj, obj->btf_custom_path ? : target_btf_path);
	err = err ? : bpf_object__load_progs(obj, extra_log_level);
	err = err ? : bpf_object_init_prog_arrays(obj);
	err = err ? : bpf_object_prepare_struct_ops(obj);

	if (obj->gen_loader) {
		/* reset FDs */
@@ -11579,22 +11612,30 @@ struct bpf_link *bpf_program__attach(const struct bpf_program *prog)
	return link;
}

struct bpf_link_struct_ops {
	struct bpf_link link;
	int map_fd;
};

static int bpf_link__detach_struct_ops(struct bpf_link *link)
{
	struct bpf_link_struct_ops *st_link;
	__u32 zero = 0;

	if (bpf_map_delete_elem(link->fd, &zero))
		return -errno;
	st_link = container_of(link, struct bpf_link_struct_ops, link);

	return 0;
	if (st_link->map_fd < 0)
		/* w/o a real link */
		return bpf_map_delete_elem(link->fd, &zero);

	return close(link->fd);
}

struct bpf_link *bpf_map__attach_struct_ops(const struct bpf_map *map)
{
	struct bpf_struct_ops *st_ops;
	struct bpf_link *link;
	__u32 i, zero = 0;
	int err;
	struct bpf_link_struct_ops *link;
	__u32 zero = 0;
	int err, fd;

	if (!bpf_map__is_struct_ops(map) || map->fd == -1)
		return libbpf_err_ptr(-EINVAL);
@@ -11603,31 +11644,37 @@ struct bpf_link *bpf_map__attach_struct_ops(const struct bpf_map *map)
	if (!link)
		return libbpf_err_ptr(-EINVAL);

	st_ops = map->st_ops;
	for (i = 0; i < btf_vlen(st_ops->type); i++) {
		struct bpf_program *prog = st_ops->progs[i];
		void *kern_data;
		int prog_fd;
	/* kern_vdata should be prepared during the loading phase. */
	err = bpf_map_update_elem(map->fd, &zero, map->st_ops->kern_vdata, 0);
	/* It can be EBUSY if the map has been used to create or
	 * update a link before.  We don't allow updating the value of
	 * a struct_ops once it is set.  That ensures that the value
	 * never changed.  So, it is safe to skip EBUSY.
	 */
	if (err && (!(map->def.map_flags & BPF_F_LINK) || err != -EBUSY)) {
		free(link);
		return libbpf_err_ptr(err);
	}

		if (!prog)
			continue;
	link->link.detach = bpf_link__detach_struct_ops;

		prog_fd = bpf_program__fd(prog);
		kern_data = st_ops->kern_vdata + st_ops->kern_func_off[i];
		*(unsigned long *)kern_data = prog_fd;
	if (!(map->def.map_flags & BPF_F_LINK)) {
		/* w/o a real link */
		link->link.fd = map->fd;
		link->map_fd = -1;
		return &link->link;
	}

	err = bpf_map_update_elem(map->fd, &zero, st_ops->kern_vdata, 0);
	if (err) {
		err = -errno;
	fd = bpf_link_create(map->fd, 0, BPF_STRUCT_OPS, NULL);
	if (fd < 0) {
		free(link);
		return libbpf_err_ptr(err);
		return libbpf_err_ptr(fd);
	}

	link->detach = bpf_link__detach_struct_ops;
	link->fd = map->fd;
	link->link.fd = fd;
	link->map_fd = map->fd;

	return link;
	return &link->link;
}

typedef enum bpf_perf_event_ret (*bpf_perf_event_print_t)(struct perf_event_header *hdr,