From: Martynas Pumputis <m@lambda.lt>
To: bpf@vger.kernel.org
Cc: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, m@lambda.lt
Subject: [PATCH bpf] libbpf: fix reuse of pinned map on older kernel
Date: Tue, 6 Jul 2021 19:26:19 +0200 [thread overview]
Message-ID: <20210706172619.579001-1-m@lambda.lt> (raw)
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@lambda.lt>
---
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 == NULL || 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
next reply other threads:[~2021-07-06 17:24 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-07-06 17:26 Martynas Pumputis [this message]
2021-07-06 23:32 ` [PATCH bpf] libbpf: fix reuse of pinned map on older kernel Song Liu
2021-07-07 10:38 ` Martynas Pumputis
2021-07-07 22:55 ` Andrii Nakryiko
2021-07-07 22:58 ` Andrii Nakryiko
2021-07-08 11:45 ` Martynas Pumputis
2021-07-08 15:01 ` Toke Høiland-Jørgensen
2021-07-08 20:20 ` Andrii Nakryiko
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20210706172619.579001-1-m@lambda.lt \
--to=m@lambda.lt \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.