Message ID | 20250505-vmlinux-mmap-v3-3-5d53afa060e8@isovalent.com |
---|---|
State | New |
Headers | show |
Series | Allow mmap of /sys/kernel/btf/vmlinux | expand |
On Mon, May 5, 2025 at 11:39 AM Lorenz Bauer <lmb@isovalent.com> wrote: > > Teach libbpf to use mmap when parsing vmlinux BTF from /sys. We don't > apply this to fall-back paths on the regular file system because there > is no way to ensure that modifications underlying the MAP_PRIVATE > mapping are not visible to the process. > > Signed-off-by: Lorenz Bauer <lmb@isovalent.com> > --- > tools/lib/bpf/btf.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++------- > 1 file changed, 72 insertions(+), 11 deletions(-) > [...] > @@ -1030,7 +1045,7 @@ struct btf *btf__new_empty_split(struct btf *base_btf) > return libbpf_ptr(btf_new_empty(base_btf)); > } > > -static struct btf *btf_new(const void *data, __u32 size, struct btf *base_btf) > +static struct btf *btf_new_no_copy(void *data, __u32 size, struct btf *base_btf) > { > struct btf *btf; > int err; > @@ -1050,12 +1065,7 @@ static struct btf *btf_new(const void *data, __u32 size, struct btf *base_btf) > btf->start_str_off = base_btf->hdr->str_len; > } > > - btf->raw_data = malloc(size); > - if (!btf->raw_data) { > - err = -ENOMEM; > - goto done; > - } > - memcpy(btf->raw_data, data, size); > + btf->raw_data = data; > btf->raw_size = size; > > btf->hdr = btf->raw_data; > @@ -1081,6 +1091,24 @@ static struct btf *btf_new(const void *data, __u32 size, struct btf *base_btf) > return btf; > } > > +static struct btf *btf_new(const void *data, __u32 size, struct btf *base_btf) btf_new() is internal, so I'd extend existing btf_new() with `bool is_mmap` and not add btf_new_no_copy(), I think it's simpler. Eventually we can turn is_mmap into some sort of flags, if we need more tuning of data ownership behavior > +{ > + struct btf *btf; > + void *raw_data; > + > + raw_data = malloc(size); > + if (!raw_data) > + return ERR_PTR(-ENOMEM); > + > + memcpy(raw_data, data, size); > + > + btf = btf_new_no_copy(raw_data, size, base_btf); > + if (IS_ERR(btf)) > + free(raw_data); > + > + return btf; > +} > + > struct btf *btf__new(const void *data, __u32 size) > { > return libbpf_ptr(btf_new(data, size, NULL)); > @@ -1354,6 +1382,37 @@ struct btf *btf__parse_raw_split(const char *path, struct btf *base_btf) > return libbpf_ptr(btf_parse_raw(path, base_btf)); > } > > +static struct btf *btf_parse_raw_mmap(const char *path, struct btf *base_btf) > +{ > + struct stat st; > + void *data; > + struct btf *btf; > + int fd; > + > + fd = open(path, O_RDONLY); > + if (fd < 0) > + return libbpf_err_ptr(-errno); > + > + if (fstat(fd, &st) < 0) { > + close(fd); close() can clobber errno, so save `err = -errno` before it > + return libbpf_err_ptr(-errno); > + } > + > + data = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); > + close(fd); same, errno clobbering danger > + > + if (data == MAP_FAILED) > + return NULL; > + > + btf = btf_new_no_copy(data, st.st_size, base_btf); > + if (!btf) btf_new_no_copy() is returning ERR_PTR() on error, no? pw-bot: cr > + munmap(data, st.st_size); > + else > + btf->raw_data_is_mmap = true; > + > + return btf; > +} > + > static struct btf *btf_parse(const char *path, struct btf *base_btf, struct btf_ext **btf_ext) > { > struct btf *btf; > @@ -1659,8 +1718,7 @@ struct btf *btf__load_from_kernel_by_id(__u32 id) > static void btf_invalidate_raw_data(struct btf *btf) > { > if (btf->raw_data) { > - free(btf->raw_data); > - btf->raw_data = NULL; > + btf_free_raw_data(btf); > } > if (btf->raw_data_swapped) { > free(btf->raw_data_swapped); > @@ -5290,7 +5348,10 @@ struct btf *btf__load_vmlinux_btf(void) > pr_warn("kernel BTF is missing at '%s', was CONFIG_DEBUG_INFO_BTF enabled?\n", > sysfs_btf_path); > } else { > - btf = btf__parse(sysfs_btf_path, NULL); > + btf = btf_parse_raw_mmap(sysfs_btf_path, NULL); > + if (IS_ERR_OR_NULL(btf)) > + btf = btf__parse(sysfs_btf_path, NULL); > + > if (!btf) { > err = -errno; > pr_warn("failed to read kernel BTF from '%s': %s\n", > > -- > 2.49.0 >
diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c index b7513d4cce55b263310c341bc254df6364e829d9..3006c1ebb97ed899eb519b10927491d87ccdaca5 100644 --- a/tools/lib/bpf/btf.c +++ b/tools/lib/bpf/btf.c @@ -12,6 +12,7 @@ #include <sys/utsname.h> #include <sys/param.h> #include <sys/stat.h> +#include <sys/mman.h> #include <linux/kernel.h> #include <linux/err.h> #include <linux/btf.h> @@ -120,6 +121,9 @@ struct btf { /* whether base_btf should be freed in btf_free for this instance */ bool owns_base; + /* whether raw_data is a (read-only) mmap */ + bool raw_data_is_mmap; + /* BTF object FD, if loaded into kernel */ int fd; @@ -951,6 +955,17 @@ static bool btf_is_modifiable(const struct btf *btf) return (void *)btf->hdr != btf->raw_data; } +static void btf_free_raw_data(struct btf *btf) +{ + if (btf->raw_data_is_mmap) { + munmap(btf->raw_data, btf->raw_size); + btf->raw_data_is_mmap = false; + } else { + free(btf->raw_data); + } + btf->raw_data = NULL; +} + void btf__free(struct btf *btf) { if (IS_ERR_OR_NULL(btf)) @@ -970,7 +985,7 @@ void btf__free(struct btf *btf) free(btf->types_data); strset__free(btf->strs_set); } - free(btf->raw_data); + btf_free_raw_data(btf); free(btf->raw_data_swapped); free(btf->type_offs); if (btf->owns_base) @@ -1030,7 +1045,7 @@ struct btf *btf__new_empty_split(struct btf *base_btf) return libbpf_ptr(btf_new_empty(base_btf)); } -static struct btf *btf_new(const void *data, __u32 size, struct btf *base_btf) +static struct btf *btf_new_no_copy(void *data, __u32 size, struct btf *base_btf) { struct btf *btf; int err; @@ -1050,12 +1065,7 @@ static struct btf *btf_new(const void *data, __u32 size, struct btf *base_btf) btf->start_str_off = base_btf->hdr->str_len; } - btf->raw_data = malloc(size); - if (!btf->raw_data) { - err = -ENOMEM; - goto done; - } - memcpy(btf->raw_data, data, size); + btf->raw_data = data; btf->raw_size = size; btf->hdr = btf->raw_data; @@ -1081,6 +1091,24 @@ static struct btf *btf_new(const void *data, __u32 size, struct btf *base_btf) return btf; } +static struct btf *btf_new(const void *data, __u32 size, struct btf *base_btf) +{ + struct btf *btf; + void *raw_data; + + raw_data = malloc(size); + if (!raw_data) + return ERR_PTR(-ENOMEM); + + memcpy(raw_data, data, size); + + btf = btf_new_no_copy(raw_data, size, base_btf); + if (IS_ERR(btf)) + free(raw_data); + + return btf; +} + struct btf *btf__new(const void *data, __u32 size) { return libbpf_ptr(btf_new(data, size, NULL)); @@ -1354,6 +1382,37 @@ struct btf *btf__parse_raw_split(const char *path, struct btf *base_btf) return libbpf_ptr(btf_parse_raw(path, base_btf)); } +static struct btf *btf_parse_raw_mmap(const char *path, struct btf *base_btf) +{ + struct stat st; + void *data; + struct btf *btf; + int fd; + + fd = open(path, O_RDONLY); + if (fd < 0) + return libbpf_err_ptr(-errno); + + if (fstat(fd, &st) < 0) { + close(fd); + return libbpf_err_ptr(-errno); + } + + data = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + close(fd); + + if (data == MAP_FAILED) + return NULL; + + btf = btf_new_no_copy(data, st.st_size, base_btf); + if (!btf) + munmap(data, st.st_size); + else + btf->raw_data_is_mmap = true; + + return btf; +} + static struct btf *btf_parse(const char *path, struct btf *base_btf, struct btf_ext **btf_ext) { struct btf *btf; @@ -1659,8 +1718,7 @@ struct btf *btf__load_from_kernel_by_id(__u32 id) static void btf_invalidate_raw_data(struct btf *btf) { if (btf->raw_data) { - free(btf->raw_data); - btf->raw_data = NULL; + btf_free_raw_data(btf); } if (btf->raw_data_swapped) { free(btf->raw_data_swapped); @@ -5290,7 +5348,10 @@ struct btf *btf__load_vmlinux_btf(void) pr_warn("kernel BTF is missing at '%s', was CONFIG_DEBUG_INFO_BTF enabled?\n", sysfs_btf_path); } else { - btf = btf__parse(sysfs_btf_path, NULL); + btf = btf_parse_raw_mmap(sysfs_btf_path, NULL); + if (IS_ERR_OR_NULL(btf)) + btf = btf__parse(sysfs_btf_path, NULL); + if (!btf) { err = -errno; pr_warn("failed to read kernel BTF from '%s': %s\n",
Teach libbpf to use mmap when parsing vmlinux BTF from /sys. We don't apply this to fall-back paths on the regular file system because there is no way to ensure that modifications underlying the MAP_PRIVATE mapping are not visible to the process. Signed-off-by: Lorenz Bauer <lmb@isovalent.com> --- tools/lib/bpf/btf.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 72 insertions(+), 11 deletions(-)