When loading a BPF program with a pinned map, the loader checks whether the pinned map can be reused, i.e. their properties match. To derive such of the pinned map, the loader invokes BPF_OBJ_GET_INFO_BY_FD and then does the comparison. Unfortunately, on < 4.12 kernels the BPF_OBJ_GET_INFO_BY_FD is not available, so loading the program fails with the following error: libbpf: failed to get map info for map FD 5: Invalid argument libbpf: couldn't reuse pinned map at '/sys/fs/bpf/tc/globals/cilium_call_policy': parameter mismatch" libbpf: map 'cilium_call_policy': error reusing pinned map libbpf: map 'cilium_call_policy': failed to create: Invalid argument(-22) libbpf: failed to load object 'bpf_overlay.o' To fix this, probe the kernel for BPF_OBJ_GET_INFO_BY_FD support. If it doesn't support, then fallback to derivation of the map properties via /proc/$PID/fdinfo/$MAP_FD. Signed-off-by: Martynas Pumputis <m@xxxxxxxxx> --- tools/lib/bpf/libbpf.c | 103 +++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 92 insertions(+), 11 deletions(-) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index ac882e1..f3daed3 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -193,6 +193,8 @@ enum kern_feature_id { FEAT_MODULE_BTF, /* BTF_KIND_FLOAT support */ FEAT_BTF_FLOAT, + /* BPF_OBJ_GET_INFO_BY_FD support */ + FEAT_OBJ_GET_INFO_BY_FD, __FEAT_CNT, }; @@ -3920,14 +3922,54 @@ static int bpf_map_find_btf_info(struct bpf_object *obj, struct bpf_map *map) return 0; } -int bpf_map__reuse_fd(struct bpf_map *map, int fd) +static int bpf_get_map_info_from_fdinfo(int fd, struct bpf_map_info *info) +{ + char file[PATH_MAX], buff[4096]; + FILE *fp; + __u32 val; + int err; + + snprintf(file, sizeof(file), "/proc/%d/fdinfo/%d", getpid(), fd); + memset(info, 0, sizeof(*info)); + + fp = fopen(file, "r"); + if (!fp) { + err = -errno; + pr_warn("failed to open %s: %d. No procfs support?\n", file, + err); + return err; + } + + while (fgets(buff, sizeof(buff), fp)) { + if (sscanf(buff, "map_type:\t%u", &val) == 1) + info->type = val; + else if (sscanf(buff, "key_size:\t%u", &val) == 1) + info->key_size = val; + else if (sscanf(buff, "value_size:\t%u", &val) == 1) + info->value_size = val; + else if (sscanf(buff, "max_entries:\t%u", &val) == 1) + info->max_entries = val; + else if (sscanf(buff, "map_flags:\t%i", &val) == 1) + info->map_flags = val; + } + + fclose(fp); + + return 0; +} + +static int bpf_map__reuse_fd_safe(struct bpf_object *obj, struct bpf_map *map, + int fd) { struct bpf_map_info info = {}; __u32 len = sizeof(info); int new_fd, err; char *new_name; - err = bpf_obj_get_info_by_fd(fd, &info, &len); + if (!obj || kernel_supports(obj, FEAT_OBJ_GET_INFO_BY_FD)) + err = bpf_obj_get_info_by_fd(fd, &info, &len); + else + err = bpf_get_map_info_from_fdinfo(fd, &info); if (err) return libbpf_err(err); @@ -3974,6 +4016,11 @@ err_free_new_name: return libbpf_err(err); } +int bpf_map__reuse_fd(struct bpf_map *map, int fd) +{ + return bpf_map__reuse_fd_safe(NULL, map, fd); +} + __u32 bpf_map__max_entries(const struct bpf_map *map) { return map->def.max_entries; @@ -4320,6 +4367,27 @@ static int probe_module_btf(void) return !err; } +static int probe_kern_bpf_get_info_by_fd(void) +{ + int fd, err; + __u32 len; + struct bpf_map_info info; + struct bpf_create_map_attr attr = { + .map_type = BPF_MAP_TYPE_ARRAY, + .key_size = sizeof(int), + .value_size = sizeof(int), + .max_entries = 1, + }; + + fd = bpf_create_map_xattr(&attr); + if (fd < 0) + return 0; + + err = bpf_obj_get_info_by_fd(fd, &info, &len); + close(fd); + return !err; +} + enum kern_feature_result { FEAT_UNKNOWN = 0, FEAT_SUPPORTED = 1, @@ -4370,6 +4438,9 @@ static struct kern_feature_desc { [FEAT_BTF_FLOAT] = { "BTF_KIND_FLOAT support", probe_kern_btf_float, }, + [FEAT_OBJ_GET_INFO_BY_FD] = { + "BPF_OBJ_GET_INFO_BY_FD support", probe_kern_bpf_get_info_by_fd, + }, }; static bool kernel_supports(const struct bpf_object *obj, enum kern_feature_id feat_id) @@ -4398,7 +4469,8 @@ static bool kernel_supports(const struct bpf_object *obj, enum kern_feature_id f return READ_ONCE(feat->res) == FEAT_SUPPORTED; } -static bool map_is_reuse_compat(const struct bpf_map *map, int map_fd) +static bool map_is_reuse_compat(struct bpf_object *obj, + const struct bpf_map *map, int map_fd) { struct bpf_map_info map_info = {}; char msg[STRERR_BUFSIZE]; @@ -4406,10 +4478,19 @@ static bool map_is_reuse_compat(const struct bpf_map *map, int map_fd) map_info_len = sizeof(map_info); - if (bpf_obj_get_info_by_fd(map_fd, &map_info, &map_info_len)) { - pr_warn("failed to get map info for map FD %d: %s\n", - map_fd, libbpf_strerror_r(errno, msg, sizeof(msg))); - return false; + if (kernel_supports(obj, FEAT_OBJ_GET_INFO_BY_FD)) { + if (bpf_obj_get_info_by_fd(map_fd, &map_info, &map_info_len)) { + pr_warn("failed to get map info for map FD %d: %s\n", + map_fd, + libbpf_strerror_r(errno, msg, sizeof(msg))); + return false; + } + } else { + if (bpf_get_map_info_from_fdinfo(map_fd, &map_info)) { + pr_warn("failed to get map info for fdinfo: %s\n", + libbpf_strerror_r(errno, msg, sizeof(msg))); + return false; + } } return (map_info.type == map->def.type && @@ -4420,7 +4501,7 @@ static bool map_is_reuse_compat(const struct bpf_map *map, int map_fd) } static int -bpf_object__reuse_map(struct bpf_map *map) +bpf_object__reuse_map(struct bpf_object *obj, struct bpf_map *map) { char *cp, errmsg[STRERR_BUFSIZE]; int err, pin_fd; @@ -4440,14 +4521,14 @@ bpf_object__reuse_map(struct bpf_map *map) return err; } - if (!map_is_reuse_compat(map, pin_fd)) { + if (!map_is_reuse_compat(obj, map, pin_fd)) { pr_warn("couldn't reuse pinned map at '%s': parameter mismatch\n", map->pin_path); close(pin_fd); return -EINVAL; } - err = bpf_map__reuse_fd(map, pin_fd); + err = bpf_map__reuse_fd_safe(obj, map, pin_fd); if (err) { close(pin_fd); return err; @@ -4643,7 +4724,7 @@ bpf_object__create_maps(struct bpf_object *obj) map = &obj->maps[i]; if (map->pin_path) { - err = bpf_object__reuse_map(map); + err = bpf_object__reuse_map(obj, map); if (err) { pr_warn("map '%s': error reusing pinned map\n", map->name); -- 2.32.0