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:
Родитель
3f51935314
Коммит
d66562fba1
|
@ -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;
|
||||
|
|
Загрузка…
Ссылка в новой задаче