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); | 	WRITE_ONCE(cpus, tmp_cpus); | ||||||
| 	return 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") | #define BPF_EMBED_OBJ(NAME, PATH) __BPF_EMBED_OBJ(NAME, PATH, 16, ".quad") | ||||||
| #endif | #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 | #ifdef __cplusplus | ||||||
| } /* extern "C" */ | } /* extern "C" */ | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | @ -213,6 +213,11 @@ LIBBPF_0.0.7 { | ||||||
| 	global: | 	global: | ||||||
| 		btf_dump__emit_type_decl; | 		btf_dump__emit_type_decl; | ||||||
| 		bpf_object__find_program_by_name; | 		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__attach; | ||||||
| 		bpf_program__name; | 		bpf_program__name; | ||||||
| 		btf__align_of; | 		btf__align_of; | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Andrii Nakryiko
						Andrii Nakryiko