diff options
Diffstat (limited to 'tools/lib/bpf/libbpf.c')
-rw-r--r-- | tools/lib/bpf/libbpf.c | 412 |
1 files changed, 339 insertions, 73 deletions
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index e176bad19bcb..8334a5a9d5d7 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -152,29 +152,36 @@ struct bpf_program { } *reloc_desc; int nr_reloc; - int fd; + struct { + int nr; + int *fds; + } instances; + bpf_program_prep_t preprocessor; struct bpf_object *obj; void *priv; bpf_program_clear_priv_t clear_priv; }; +struct bpf_map { + int fd; + char *name; + struct bpf_map_def def; + void *priv; + bpf_map_clear_priv_t clear_priv; +}; + static LIST_HEAD(bpf_objects_list); struct bpf_object { char license[64]; u32 kern_version; - void *maps_buf; - size_t maps_buf_sz; struct bpf_program *programs; size_t nr_programs; - int *map_fds; - /* - * This field is required because maps_buf will be freed and - * maps_buf_sz will be set to 0 after loaded. - */ - size_t nr_map_fds; + struct bpf_map *maps; + size_t nr_maps; + bool loaded; /* @@ -188,6 +195,7 @@ struct bpf_object { Elf *elf; GElf_Ehdr ehdr; Elf_Data *symbols; + size_t strtabidx; struct { GElf_Shdr shdr; Elf_Data *data; @@ -206,10 +214,25 @@ struct bpf_object { static void bpf_program__unload(struct bpf_program *prog) { + int i; + if (!prog) return; - zclose(prog->fd); + /* + * If the object is opened but the program was never loaded, + * it is possible that prog->instances.nr == -1. + */ + if (prog->instances.nr > 0) { + for (i = 0; i < prog->instances.nr; i++) + zclose(prog->instances.fds[i]); + } else if (prog->instances.nr != -1) { + pr_warning("Internal error: instances.nr is %d\n", + prog->instances.nr); + } + + prog->instances.nr = -1; + zfree(&prog->instances.fds); } static void bpf_program__exit(struct bpf_program *prog) @@ -260,7 +283,8 @@ bpf_program__init(void *data, size_t size, char *name, int idx, memcpy(prog->insns, data, prog->insns_cnt * sizeof(struct bpf_insn)); prog->idx = idx; - prog->fd = -1; + prog->instances.fds = NULL; + prog->instances.nr = -1; return 0; errout: @@ -469,21 +493,77 @@ static int bpf_object__init_maps(struct bpf_object *obj, void *data, size_t size) { - if (size == 0) { + size_t nr_maps; + int i; + + nr_maps = size / sizeof(struct bpf_map_def); + if (!data || !nr_maps) { pr_debug("%s doesn't need map definition\n", obj->path); return 0; } - obj->maps_buf = malloc(size); - if (!obj->maps_buf) { - pr_warning("malloc maps failed: %s\n", obj->path); + pr_debug("maps in %s: %zd bytes\n", obj->path, size); + + obj->maps = calloc(nr_maps, sizeof(obj->maps[0])); + if (!obj->maps) { + pr_warning("alloc maps for object failed\n"); return -ENOMEM; } + obj->nr_maps = nr_maps; - obj->maps_buf_sz = size; - memcpy(obj->maps_buf, data, size); - pr_debug("maps in %s: %ld bytes\n", obj->path, (long)size); + for (i = 0; i < nr_maps; i++) { + struct bpf_map_def *def = &obj->maps[i].def; + + /* + * fill all fd with -1 so won't close incorrect + * fd (fd=0 is stdin) when failure (zclose won't close + * negative fd)). + */ + obj->maps[i].fd = -1; + + /* Save map definition into obj->maps */ + *def = ((struct bpf_map_def *)data)[i]; + } + return 0; +} + +static int +bpf_object__init_maps_name(struct bpf_object *obj, int maps_shndx) +{ + int i; + Elf_Data *symbols = obj->efile.symbols; + + if (!symbols || maps_shndx < 0) + return -EINVAL; + + for (i = 0; i < symbols->d_size / sizeof(GElf_Sym); i++) { + GElf_Sym sym; + size_t map_idx; + const char *map_name; + + if (!gelf_getsym(symbols, i, &sym)) + continue; + if (sym.st_shndx != maps_shndx) + continue; + + map_name = elf_strptr(obj->efile.elf, + obj->efile.strtabidx, + sym.st_name); + map_idx = sym.st_value / sizeof(struct bpf_map_def); + if (map_idx >= obj->nr_maps) { + pr_warning("index of map \"%s\" is buggy: %zu > %zu\n", + map_name, map_idx, obj->nr_maps); + continue; + } + obj->maps[map_idx].name = strdup(map_name); + if (!obj->maps[map_idx].name) { + pr_warning("failed to alloc map name\n"); + return -ENOMEM; + } + pr_debug("map %zu is \"%s\"\n", map_idx, + obj->maps[map_idx].name); + } return 0; } @@ -492,7 +572,7 @@ static int bpf_object__elf_collect(struct bpf_object *obj) Elf *elf = obj->efile.elf; GElf_Ehdr *ep = &obj->efile.ehdr; Elf_Scn *scn = NULL; - int idx = 0, err = 0; + int idx = 0, err = 0, maps_shndx = -1; /* Elf is corrupted/truncated, avoid calling elf_strptr. */ if (!elf_rawdata(elf_getscn(elf, ep->e_shstrndx), NULL)) { @@ -542,16 +622,19 @@ static int bpf_object__elf_collect(struct bpf_object *obj) err = bpf_object__init_kversion(obj, data->d_buf, data->d_size); - else if (strcmp(name, "maps") == 0) + else if (strcmp(name, "maps") == 0) { err = bpf_object__init_maps(obj, data->d_buf, data->d_size); - else if (sh.sh_type == SHT_SYMTAB) { + maps_shndx = idx; + } else if (sh.sh_type == SHT_SYMTAB) { if (obj->efile.symbols) { pr_warning("bpf: multiple SYMTAB in %s\n", obj->path); err = -LIBBPF_ERRNO__FORMAT; - } else + } else { obj->efile.symbols = data; + obj->efile.strtabidx = sh.sh_link; + } } else if ((sh.sh_type == SHT_PROGBITS) && (sh.sh_flags & SHF_EXECINSTR) && (data->d_size > 0)) { @@ -586,6 +669,13 @@ static int bpf_object__elf_collect(struct bpf_object *obj) if (err) goto out; } + + if (!obj->efile.strtabidx || obj->efile.strtabidx >= idx) { + pr_warning("Corrupted ELF file: index of strtab invalid\n"); + return LIBBPF_ERRNO__FORMAT; + } + if (maps_shndx >= 0) + err = bpf_object__init_maps_name(obj, maps_shndx); out: return err; } @@ -668,37 +758,15 @@ static int bpf_object__create_maps(struct bpf_object *obj) { unsigned int i; - size_t nr_maps; - int *pfd; - - nr_maps = obj->maps_buf_sz / sizeof(struct bpf_map_def); - if (!obj->maps_buf || !nr_maps) { - pr_debug("don't need create maps for %s\n", - obj->path); - return 0; - } - - obj->map_fds = malloc(sizeof(int) * nr_maps); - if (!obj->map_fds) { - pr_warning("realloc perf_bpf_map_fds failed\n"); - return -ENOMEM; - } - obj->nr_map_fds = nr_maps; - /* fill all fd with -1 */ - memset(obj->map_fds, -1, sizeof(int) * nr_maps); - - pfd = obj->map_fds; - for (i = 0; i < nr_maps; i++) { - struct bpf_map_def def; + for (i = 0; i < obj->nr_maps; i++) { + struct bpf_map_def *def = &obj->maps[i].def; + int *pfd = &obj->maps[i].fd; - def = *(struct bpf_map_def *)(obj->maps_buf + - i * sizeof(struct bpf_map_def)); - - *pfd = bpf_create_map(def.type, - def.key_size, - def.value_size, - def.max_entries); + *pfd = bpf_create_map(def->type, + def->key_size, + def->value_size, + def->max_entries); if (*pfd < 0) { size_t j; int err = *pfd; @@ -706,22 +774,17 @@ bpf_object__create_maps(struct bpf_object *obj) pr_warning("failed to create map: %s\n", strerror(errno)); for (j = 0; j < i; j++) - zclose(obj->map_fds[j]); - obj->nr_map_fds = 0; - zfree(&obj->map_fds); + zclose(obj->maps[j].fd); return err; } pr_debug("create map: fd=%d\n", *pfd); - pfd++; } - zfree(&obj->maps_buf); - obj->maps_buf_sz = 0; return 0; } static int -bpf_program__relocate(struct bpf_program *prog, int *map_fds) +bpf_program__relocate(struct bpf_program *prog, struct bpf_object *obj) { int i; @@ -741,7 +804,7 @@ bpf_program__relocate(struct bpf_program *prog, int *map_fds) return -LIBBPF_ERRNO__RELOC; } insns[insn_idx].src_reg = BPF_PSEUDO_MAP_FD; - insns[insn_idx].imm = map_fds[map_idx]; + insns[insn_idx].imm = obj->maps[map_idx].fd; } zfree(&prog->reloc_desc); @@ -760,7 +823,7 @@ bpf_object__relocate(struct bpf_object *obj) for (i = 0; i < obj->nr_programs; i++) { prog = &obj->programs[i]; - err = bpf_program__relocate(prog, obj->map_fds); + err = bpf_program__relocate(prog, obj); if (err) { pr_warning("failed to relocate '%s'\n", prog->section_name); @@ -784,8 +847,7 @@ static int bpf_object__collect_reloc(struct bpf_object *obj) Elf_Data *data = obj->efile.reloc[i].data; int idx = shdr->sh_info; struct bpf_program *prog; - size_t nr_maps = obj->maps_buf_sz / - sizeof(struct bpf_map_def); + size_t nr_maps = obj->nr_maps; if (shdr->sh_type != SHT_REL) { pr_warning("internal error at %d\n", __LINE__); @@ -860,13 +922,73 @@ static int bpf_program__load(struct bpf_program *prog, char *license, u32 kern_version) { - int err, fd; + int err = 0, fd, i; - err = load_program(prog->insns, prog->insns_cnt, - license, kern_version, &fd); - if (!err) - prog->fd = fd; + if (prog->instances.nr < 0 || !prog->instances.fds) { + if (prog->preprocessor) { + pr_warning("Internal error: can't load program '%s'\n", + prog->section_name); + return -LIBBPF_ERRNO__INTERNAL; + } + + prog->instances.fds = malloc(sizeof(int)); + if (!prog->instances.fds) { + pr_warning("Not enough memory for BPF fds\n"); + return -ENOMEM; + } + prog->instances.nr = 1; + prog->instances.fds[0] = -1; + } + + if (!prog->preprocessor) { + if (prog->instances.nr != 1) { + pr_warning("Program '%s' is inconsistent: nr(%d) != 1\n", + prog->section_name, prog->instances.nr); + } + err = load_program(prog->insns, prog->insns_cnt, + license, kern_version, &fd); + if (!err) + prog->instances.fds[0] = fd; + goto out; + } + + for (i = 0; i < prog->instances.nr; i++) { + struct bpf_prog_prep_result result; + bpf_program_prep_t preprocessor = prog->preprocessor; + + bzero(&result, sizeof(result)); + err = preprocessor(prog, i, prog->insns, + prog->insns_cnt, &result); + if (err) { + pr_warning("Preprocessing the %dth instance of program '%s' failed\n", + i, prog->section_name); + goto out; + } + + if (!result.new_insn_ptr || !result.new_insn_cnt) { + pr_debug("Skip loading the %dth instance of program '%s'\n", + i, prog->section_name); + prog->instances.fds[i] = -1; + if (result.pfd) + *result.pfd = -1; + continue; + } + + err = load_program(result.new_insn_ptr, + result.new_insn_cnt, + license, kern_version, &fd); + + if (err) { + pr_warning("Loading the %dth instance of program '%s' failed\n", + i, prog->section_name); + goto out; + } + if (result.pfd) + *result.pfd = fd; + prog->instances.fds[i] = fd; + } +out: if (err) pr_warning("failed to load program '%s'\n", prog->section_name); @@ -970,10 +1092,8 @@ int bpf_object__unload(struct bpf_object *obj) if (!obj) return -EINVAL; - for (i = 0; i < obj->nr_map_fds; i++) - zclose(obj->map_fds[i]); - zfree(&obj->map_fds); - obj->nr_map_fds = 0; + for (i = 0; i < obj->nr_maps; i++) + zclose(obj->maps[i].fd); for (i = 0; i < obj->nr_programs; i++) bpf_program__unload(&obj->programs[i]); @@ -1016,7 +1136,16 @@ void bpf_object__close(struct bpf_object *obj) bpf_object__elf_finish(obj); bpf_object__unload(obj); - zfree(&obj->maps_buf); + for (i = 0; i < obj->nr_maps; i++) { + zfree(&obj->maps[i].name); + if (obj->maps[i].clear_priv) + obj->maps[i].clear_priv(&obj->maps[i], + obj->maps[i].priv); + obj->maps[i].priv = NULL; + obj->maps[i].clear_priv = NULL; + } + zfree(&obj->maps); + obj->nr_maps = 0; if (obj->programs && obj->nr_programs) { for (i = 0; i < obj->nr_programs; i++) @@ -1121,5 +1250,142 @@ const char *bpf_program__title(struct bpf_program *prog, bool needs_copy) int bpf_program__fd(struct bpf_program *prog) { - return prog->fd; + return bpf_program__nth_fd(prog, 0); +} + +int bpf_program__set_prep(struct bpf_program *prog, int nr_instances, + bpf_program_prep_t prep) +{ + int *instances_fds; + + if (nr_instances <= 0 || !prep) + return -EINVAL; + + if (prog->instances.nr > 0 || prog->instances.fds) { + pr_warning("Can't set pre-processor after loading\n"); + return -EINVAL; + } + + instances_fds = malloc(sizeof(int) * nr_instances); + if (!instances_fds) { + pr_warning("alloc memory failed for fds\n"); + return -ENOMEM; + } + + /* fill all fd with -1 */ + memset(instances_fds, -1, sizeof(int) * nr_instances); + + prog->instances.nr = nr_instances; + prog->instances.fds = instances_fds; + prog->preprocessor = prep; + return 0; +} + +int bpf_program__nth_fd(struct bpf_program *prog, int n) +{ + int fd; + + if (n >= prog->instances.nr || n < 0) { + pr_warning("Can't get the %dth fd from program %s: only %d instances\n", + n, prog->section_name, prog->instances.nr); + return -EINVAL; + } + + fd = prog->instances.fds[n]; + if (fd < 0) { + pr_warning("%dth instance of program '%s' is invalid\n", + n, prog->section_name); + return -ENOENT; + } + + return fd; +} + +int bpf_map__get_fd(struct bpf_map *map) +{ + if (!map) + return -EINVAL; + + return map->fd; +} + +int bpf_map__get_def(struct bpf_map *map, struct bpf_map_def *pdef) +{ + if (!map || !pdef) + return -EINVAL; + + *pdef = map->def; + return 0; +} + +const char *bpf_map__get_name(struct bpf_map *map) +{ + if (!map) + return NULL; + return map->name; +} + +int bpf_map__set_private(struct bpf_map *map, void *priv, + bpf_map_clear_priv_t clear_priv) +{ + if (!map) + return -EINVAL; + + if (map->priv) { + if (map->clear_priv) + map->clear_priv(map, map->priv); + } + + map->priv = priv; + map->clear_priv = clear_priv; + return 0; +} + +int bpf_map__get_private(struct bpf_map *map, void **ppriv) +{ + if (!map) + return -EINVAL; + + if (ppriv) + *ppriv = map->priv; + return 0; +} + +struct bpf_map * +bpf_map__next(struct bpf_map *prev, struct bpf_object *obj) +{ + size_t idx; + struct bpf_map *s, *e; + + if (!obj || !obj->maps) + return NULL; + + s = obj->maps; + e = obj->maps + obj->nr_maps; + + if (prev == NULL) + return s; + + if ((prev < s) || (prev >= e)) { + pr_warning("error in %s: map handler doesn't belong to object\n", + __func__); + return NULL; + } + + idx = (prev - obj->maps) + 1; + if (idx >= obj->nr_maps) + return NULL; + return &obj->maps[idx]; +} + +struct bpf_map * +bpf_object__get_map_by_name(struct bpf_object *obj, const char *name) +{ + struct bpf_map *pos; + + bpf_map__for_each(pos, obj) { + if (pos->name && !strcmp(pos->name, name)) + return pos; + } + return NULL; } |