forked from mirrors/linux
		
	libbpf: Add BPF object skeleton support
Add new set of APIs, allowing to open/load/attach BPF object through BPF object skeleton, generated by bpftool for a specific BPF object file. All the xxx_skeleton() APIs wrap up corresponding bpf_object_xxx() APIs, but additionally also automate map/program lookups by name, global data initialization and mmap()-ing, etc. All this greatly improves and simplifies userspace usability of working with BPF programs. See follow up patches for examples. Signed-off-by: Andrii Nakryiko <andriin@fb.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Acked-by: Martin KaFai Lau <kafai@fb.com> Link: https://lore.kernel.org/bpf/20191214014341.3442258-13-andriin@fb.com
This commit is contained in:
		
							parent
							
								
									3f51935314
								
							
						
					
					
						commit
						d66562fba1
					
				
					 3 changed files with 205 additions and 0 deletions
				
			
		|  | @ -6793,3 +6793,165 @@ int libbpf_num_possible_cpus(void) | |||
| 	WRITE_ONCE(cpus, tmp_cpus); | ||||
| 	return tmp_cpus; | ||||
| } | ||||
| 
 | ||||
| int bpf_object__open_skeleton(struct bpf_object_skeleton *s, | ||||
| 			      const struct bpf_object_open_opts *opts) | ||||
| { | ||||
| 	DECLARE_LIBBPF_OPTS(bpf_object_open_opts, skel_opts, | ||||
| 		.object_name = s->name, | ||||
| 	); | ||||
| 	struct bpf_object *obj; | ||||
| 	int i; | ||||
| 
 | ||||
| 	/* Attempt to preserve opts->object_name, unless overriden by user
 | ||||
| 	 * explicitly. Overwriting object name for skeletons is discouraged, | ||||
| 	 * as it breaks global data maps, because they contain object name | ||||
| 	 * prefix as their own map name prefix. When skeleton is generated, | ||||
| 	 * bpftool is making an assumption that this name will stay the same. | ||||
| 	 */ | ||||
| 	if (opts) { | ||||
| 		memcpy(&skel_opts, opts, sizeof(*opts)); | ||||
| 		if (!opts->object_name) | ||||
| 			skel_opts.object_name = s->name; | ||||
| 	} | ||||
| 
 | ||||
| 	obj = bpf_object__open_mem(s->data, s->data_sz, &skel_opts); | ||||
| 	if (IS_ERR(obj)) { | ||||
| 		pr_warn("failed to initialize skeleton BPF object '%s': %ld\n", | ||||
| 			s->name, PTR_ERR(obj)); | ||||
| 		return PTR_ERR(obj); | ||||
| 	} | ||||
| 
 | ||||
| 	*s->obj = obj; | ||||
| 
 | ||||
| 	for (i = 0; i < s->map_cnt; i++) { | ||||
| 		struct bpf_map **map = s->maps[i].map; | ||||
| 		const char *name = s->maps[i].name; | ||||
| 		void **mmaped = s->maps[i].mmaped; | ||||
| 
 | ||||
| 		*map = bpf_object__find_map_by_name(obj, name); | ||||
| 		if (!*map) { | ||||
| 			pr_warn("failed to find skeleton map '%s'\n", name); | ||||
| 			return -ESRCH; | ||||
| 		} | ||||
| 
 | ||||
| 		if (mmaped) | ||||
| 			*mmaped = (*map)->mmaped; | ||||
| 	} | ||||
| 
 | ||||
| 	for (i = 0; i < s->prog_cnt; i++) { | ||||
| 		struct bpf_program **prog = s->progs[i].prog; | ||||
| 		const char *name = s->progs[i].name; | ||||
| 
 | ||||
| 		*prog = bpf_object__find_program_by_name(obj, name); | ||||
| 		if (!*prog) { | ||||
| 			pr_warn("failed to find skeleton program '%s'\n", name); | ||||
| 			return -ESRCH; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int bpf_object__load_skeleton(struct bpf_object_skeleton *s) | ||||
| { | ||||
| 	int i, err; | ||||
| 
 | ||||
| 	err = bpf_object__load(*s->obj); | ||||
| 	if (err) { | ||||
| 		pr_warn("failed to load BPF skeleton '%s': %d\n", s->name, err); | ||||
| 		return err; | ||||
| 	} | ||||
| 
 | ||||
| 	for (i = 0; i < s->map_cnt; i++) { | ||||
| 		struct bpf_map *map = *s->maps[i].map; | ||||
| 		size_t mmap_sz = bpf_map_mmap_sz(map); | ||||
| 		int prot, map_fd = bpf_map__fd(map); | ||||
| 		void **mmaped = s->maps[i].mmaped; | ||||
| 		void *remapped; | ||||
| 
 | ||||
| 		if (!mmaped) | ||||
| 			continue; | ||||
| 
 | ||||
| 		if (!(map->def.map_flags & BPF_F_MMAPABLE)) { | ||||
| 			*mmaped = NULL; | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		if (map->def.map_flags & BPF_F_RDONLY_PROG) | ||||
| 			prot = PROT_READ; | ||||
| 		else | ||||
| 			prot = PROT_READ | PROT_WRITE; | ||||
| 
 | ||||
| 		/* Remap anonymous mmap()-ed "map initialization image" as
 | ||||
| 		 * a BPF map-backed mmap()-ed memory, but preserving the same | ||||
| 		 * memory address. This will cause kernel to change process' | ||||
| 		 * page table to point to a different piece of kernel memory, | ||||
| 		 * but from userspace point of view memory address (and its | ||||
| 		 * contents, being identical at this point) will stay the | ||||
| 		 * same. This mapping will be released by bpf_object__close() | ||||
| 		 * as per normal clean up procedure, so we don't need to worry | ||||
| 		 * about it from skeleton's clean up perspective. | ||||
| 		 */ | ||||
| 		remapped = mmap(*mmaped, mmap_sz, prot, MAP_SHARED | MAP_FIXED, | ||||
| 				map_fd, 0); | ||||
| 		if (remapped == MAP_FAILED) { | ||||
| 			err = -errno; | ||||
| 			*mmaped = NULL; | ||||
| 			pr_warn("failed to re-mmap() map '%s': %d\n", | ||||
| 				 bpf_map__name(map), err); | ||||
| 			return err; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int bpf_object__attach_skeleton(struct bpf_object_skeleton *s) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	for (i = 0; i < s->prog_cnt; i++) { | ||||
| 		struct bpf_program *prog = *s->progs[i].prog; | ||||
| 		struct bpf_link **link = s->progs[i].link; | ||||
| 		const struct bpf_sec_def *sec_def; | ||||
| 		const char *sec_name = bpf_program__title(prog, false); | ||||
| 
 | ||||
| 		sec_def = find_sec_def(sec_name); | ||||
| 		if (!sec_def || !sec_def->attach_fn) | ||||
| 			continue; | ||||
| 
 | ||||
| 		*link = sec_def->attach_fn(sec_def, prog); | ||||
| 		if (IS_ERR(*link)) { | ||||
| 			pr_warn("failed to auto-attach program '%s': %ld\n", | ||||
| 				bpf_program__name(prog), PTR_ERR(*link)); | ||||
| 			return PTR_ERR(*link); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| void bpf_object__detach_skeleton(struct bpf_object_skeleton *s) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	for (i = 0; i < s->prog_cnt; i++) { | ||||
| 		struct bpf_link **link = s->progs[i].link; | ||||
| 
 | ||||
| 		if (!IS_ERR_OR_NULL(*link)) | ||||
| 			bpf_link__destroy(*link); | ||||
| 		*link = NULL; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void bpf_object__destroy_skeleton(struct bpf_object_skeleton *s) | ||||
| { | ||||
| 	if (s->progs) | ||||
| 		bpf_object__detach_skeleton(s); | ||||
| 	if (s->obj) | ||||
| 		bpf_object__close(*s->obj); | ||||
| 	free(s->maps); | ||||
| 	free(s->progs); | ||||
| 	free(s); | ||||
| } | ||||
|  |  | |||
|  | @ -631,6 +631,44 @@ BPF_EMBED_OBJ_DECLARE(NAME) | |||
| #define BPF_EMBED_OBJ(NAME, PATH) __BPF_EMBED_OBJ(NAME, PATH, 16, ".quad") | ||||
| #endif | ||||
| 
 | ||||
| struct bpf_map_skeleton { | ||||
| 	const char *name; | ||||
| 	struct bpf_map **map; | ||||
| 	void **mmaped; | ||||
| }; | ||||
| 
 | ||||
| struct bpf_prog_skeleton { | ||||
| 	const char *name; | ||||
| 	struct bpf_program **prog; | ||||
| 	struct bpf_link **link; | ||||
| }; | ||||
| 
 | ||||
| struct bpf_object_skeleton { | ||||
| 	size_t sz; /* size of this struct, for forward/backward compatibility */ | ||||
| 
 | ||||
| 	const char *name; | ||||
| 	void *data; | ||||
| 	size_t data_sz; | ||||
| 
 | ||||
| 	struct bpf_object **obj; | ||||
| 
 | ||||
| 	int map_cnt; | ||||
| 	int map_skel_sz; /* sizeof(struct bpf_skeleton_map) */ | ||||
| 	struct bpf_map_skeleton *maps; | ||||
| 
 | ||||
| 	int prog_cnt; | ||||
| 	int prog_skel_sz; /* sizeof(struct bpf_skeleton_prog) */ | ||||
| 	struct bpf_prog_skeleton *progs; | ||||
| }; | ||||
| 
 | ||||
| LIBBPF_API int | ||||
| bpf_object__open_skeleton(struct bpf_object_skeleton *s, | ||||
| 			  const struct bpf_object_open_opts *opts); | ||||
| LIBBPF_API int bpf_object__load_skeleton(struct bpf_object_skeleton *s); | ||||
| LIBBPF_API int bpf_object__attach_skeleton(struct bpf_object_skeleton *s); | ||||
| LIBBPF_API void bpf_object__detach_skeleton(struct bpf_object_skeleton *s); | ||||
| LIBBPF_API void bpf_object__destroy_skeleton(struct bpf_object_skeleton *s); | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } /* extern "C" */ | ||||
| #endif | ||||
|  |  | |||
|  | @ -213,6 +213,11 @@ LIBBPF_0.0.7 { | |||
| 	global: | ||||
| 		btf_dump__emit_type_decl; | ||||
| 		bpf_object__find_program_by_name; | ||||
| 		bpf_object__attach_skeleton; | ||||
| 		bpf_object__destroy_skeleton; | ||||
| 		bpf_object__detach_skeleton; | ||||
| 		bpf_object__load_skeleton; | ||||
| 		bpf_object__open_skeleton; | ||||
| 		bpf_program__attach; | ||||
| 		bpf_program__name; | ||||
| 		btf__align_of; | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Andrii Nakryiko
						Andrii Nakryiko