Skip to content

Commit 2e7ba4f

Browse files
committed
libbpf: Recognize __arena global variables.
LLVM automatically places __arena variables into ".arena.1" ELF section. In order to use such global variables bpf program must include definition of arena map in ".maps" section, like: struct { __uint(type, BPF_MAP_TYPE_ARENA); __uint(map_flags, BPF_F_MMAPABLE); __uint(max_entries, 1000); /* number of pages */ __ulong(map_extra, 2ull << 44); /* start of mmap() region */ } arena SEC(".maps"); libbpf recognizes both uses of arena and creates single `struct bpf_map *` instance in libbpf APIs. ".arena.1" ELF section data is used as initial data image, which is exposed through skeleton and bpf_map__initial_value() to the user, if they need to tune it before the load phase. During load phase, this initial image is copied over into mmap()'ed region corresponding to arena, and discarded. Few small checks here and there had to be added to make sure this approach works with bpf_map__initial_value(), mostly due to hard-coded assumption that map->mmaped is set up with mmap() syscall and should be munmap()'ed. For arena, .arena.1 can be (much) smaller than maximum arena size, so this smaller data size has to be tracked separately. Given it is enforced that there is only one arena for entire bpf_object instance, we just keep it in a separate field. This can be generalized if necessary later. All global variables from ".arena.1" section are accessible from user space via skel->arena->name_of_var. For bss/data/rodata the skeleton/libbpf perform the following sequence: 1. addr = mmap(MAP_ANONYMOUS) 2. user space optionally modifies global vars 3. map_fd = bpf_create_map() 4. bpf_update_map_elem(map_fd, addr) // to store values into the kernel 5. mmap(addr, MAP_FIXED, map_fd) after step 5 user spaces see the values it wrote at step 2 at the same addresses arena doesn't support update_map_elem. Hence skeleton/libbpf do: 1. addr = malloc(sizeof SEC ".arena.1") 2. user space optionally modifies global vars 3. map_fd = bpf_create_map(MAP_TYPE_ARENA) 4. real_addr = mmap(map->map_extra, MAP_SHARED | MAP_FIXED, map_fd) 5. memcpy(real_addr, addr) // this will fault-in and allocate pages At the end look and feel of global data vs __arena global data is the same from bpf prog pov. Another complication is: struct { __uint(type, BPF_MAP_TYPE_ARENA); } arena SEC(".maps"); int __arena foo; int bar; ptr1 = &foo; // relocation against ".arena.1" section ptr2 = &arena; // relocation against ".maps" section ptr3 = &bar; // relocation against ".bss" section Fo the kernel ptr1 and ptr2 has point to the same arena's map_fd while ptr3 points to a different global array's map_fd. For the verifier: ptr1->type == unknown_scalar ptr2->type == const_ptr_to_map ptr3->type == ptr_to_map_value After verification, from JIT pov all 3 ptr-s are normal ld_imm64 insns. Signed-off-by: Andrii Nakryiko <[email protected]> Signed-off-by: Alexei Starovoitov <[email protected]> Signed-off-by: Andrii Nakryiko <[email protected]> Acked-by: Quentin Monnet <[email protected]> Link: https://lore.kernel.org/bpf/[email protected]
1 parent eed512e commit 2e7ba4f

File tree

3 files changed

+120
-13
lines changed

3 files changed

+120
-13
lines changed

tools/bpf/bpftool/gen.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,12 @@ static bool get_datasec_ident(const char *sec_name, char *buf, size_t buf_sz)
120120
static const char *pfxs[] = { ".data", ".rodata", ".bss", ".kconfig" };
121121
int i, n;
122122

123+
/* recognize hard coded LLVM section name */
124+
if (strcmp(sec_name, ".arena.1") == 0) {
125+
/* this is the name to use in skeleton */
126+
snprintf(buf, buf_sz, "arena");
127+
return true;
128+
}
123129
for (i = 0, n = ARRAY_SIZE(pfxs); i < n; i++) {
124130
const char *pfx = pfxs[i];
125131

@@ -250,6 +256,13 @@ static const struct btf_type *find_type_for_map(struct btf *btf, const char *map
250256

251257
static bool is_mmapable_map(const struct bpf_map *map, char *buf, size_t sz)
252258
{
259+
size_t tmp_sz;
260+
261+
if (bpf_map__type(map) == BPF_MAP_TYPE_ARENA && bpf_map__initial_value(map, &tmp_sz)) {
262+
snprintf(buf, sz, "arena");
263+
return true;
264+
}
265+
253266
if (!bpf_map__is_internal(map) || !(bpf_map__map_flags(map) & BPF_F_MMAPABLE))
254267
return false;
255268

tools/lib/bpf/libbpf.c

Lines changed: 106 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,7 @@ struct bpf_struct_ops {
498498
#define KSYMS_SEC ".ksyms"
499499
#define STRUCT_OPS_SEC ".struct_ops"
500500
#define STRUCT_OPS_LINK_SEC ".struct_ops.link"
501+
#define ARENA_SEC ".arena.1"
501502

502503
enum libbpf_map_type {
503504
LIBBPF_MAP_UNSPEC,
@@ -629,6 +630,7 @@ struct elf_state {
629630
Elf *elf;
630631
Elf64_Ehdr *ehdr;
631632
Elf_Data *symbols;
633+
Elf_Data *arena_data;
632634
size_t shstrndx; /* section index for section name strings */
633635
size_t strtabidx;
634636
struct elf_sec_desc *secs;
@@ -638,6 +640,7 @@ struct elf_state {
638640
int text_shndx;
639641
int symbols_shndx;
640642
bool has_st_ops;
643+
int arena_data_shndx;
641644
};
642645

643646
struct usdt_manager;
@@ -697,6 +700,10 @@ struct bpf_object {
697700

698701
struct usdt_manager *usdt_man;
699702

703+
struct bpf_map *arena_map;
704+
void *arena_data;
705+
size_t arena_data_sz;
706+
700707
struct kern_feature_cache *feat_cache;
701708
char *token_path;
702709
int token_fd;
@@ -1443,6 +1450,7 @@ static void bpf_object__elf_finish(struct bpf_object *obj)
14431450
elf_end(obj->efile.elf);
14441451
obj->efile.elf = NULL;
14451452
obj->efile.symbols = NULL;
1453+
obj->efile.arena_data = NULL;
14461454

14471455
zfree(&obj->efile.secs);
14481456
obj->efile.sec_cnt = 0;
@@ -1851,7 +1859,7 @@ bpf_object__init_internal_map(struct bpf_object *obj, enum libbpf_map_type type,
18511859
def->value_size = data_sz;
18521860
def->max_entries = 1;
18531861
def->map_flags = type == LIBBPF_MAP_RODATA || type == LIBBPF_MAP_KCONFIG
1854-
? BPF_F_RDONLY_PROG : 0;
1862+
? BPF_F_RDONLY_PROG : 0;
18551863

18561864
/* failures are fine because of maps like .rodata.str1.1 */
18571865
(void) map_fill_btf_type_info(obj, map);
@@ -2843,6 +2851,32 @@ static int bpf_object__init_user_btf_map(struct bpf_object *obj,
28432851
return 0;
28442852
}
28452853

2854+
static int init_arena_map_data(struct bpf_object *obj, struct bpf_map *map,
2855+
const char *sec_name, int sec_idx,
2856+
void *data, size_t data_sz)
2857+
{
2858+
const long page_sz = sysconf(_SC_PAGE_SIZE);
2859+
size_t mmap_sz;
2860+
2861+
mmap_sz = bpf_map_mmap_sz(obj->arena_map);
2862+
if (roundup(data_sz, page_sz) > mmap_sz) {
2863+
pr_warn("elf: sec '%s': declared ARENA map size (%zu) is too small to hold global __arena variables of size %zu\n",
2864+
sec_name, mmap_sz, data_sz);
2865+
return -E2BIG;
2866+
}
2867+
2868+
obj->arena_data = malloc(data_sz);
2869+
if (!obj->arena_data)
2870+
return -ENOMEM;
2871+
memcpy(obj->arena_data, data, data_sz);
2872+
obj->arena_data_sz = data_sz;
2873+
2874+
/* make bpf_map__init_value() work for ARENA maps */
2875+
map->mmaped = obj->arena_data;
2876+
2877+
return 0;
2878+
}
2879+
28462880
static int bpf_object__init_user_btf_maps(struct bpf_object *obj, bool strict,
28472881
const char *pin_root_path)
28482882
{
@@ -2892,6 +2926,33 @@ static int bpf_object__init_user_btf_maps(struct bpf_object *obj, bool strict,
28922926
return err;
28932927
}
28942928

2929+
for (i = 0; i < obj->nr_maps; i++) {
2930+
struct bpf_map *map = &obj->maps[i];
2931+
2932+
if (map->def.type != BPF_MAP_TYPE_ARENA)
2933+
continue;
2934+
2935+
if (obj->arena_map) {
2936+
pr_warn("map '%s': only single ARENA map is supported (map '%s' is also ARENA)\n",
2937+
map->name, obj->arena_map->name);
2938+
return -EINVAL;
2939+
}
2940+
obj->arena_map = map;
2941+
2942+
if (obj->efile.arena_data) {
2943+
err = init_arena_map_data(obj, map, ARENA_SEC, obj->efile.arena_data_shndx,
2944+
obj->efile.arena_data->d_buf,
2945+
obj->efile.arena_data->d_size);
2946+
if (err)
2947+
return err;
2948+
}
2949+
}
2950+
if (obj->efile.arena_data && !obj->arena_map) {
2951+
pr_warn("elf: sec '%s': to use global __arena variables the ARENA map should be explicitly declared in SEC(\".maps\")\n",
2952+
ARENA_SEC);
2953+
return -ENOENT;
2954+
}
2955+
28952956
return 0;
28962957
}
28972958

@@ -3771,6 +3832,9 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
37713832
sec_desc->shdr = sh;
37723833
sec_desc->data = data;
37733834
obj->efile.has_st_ops = true;
3835+
} else if (strcmp(name, ARENA_SEC) == 0) {
3836+
obj->efile.arena_data = data;
3837+
obj->efile.arena_data_shndx = idx;
37743838
} else {
37753839
pr_info("elf: skipping unrecognized data section(%d) %s\n",
37763840
idx, name);
@@ -4400,6 +4464,15 @@ static int bpf_program__record_reloc(struct bpf_program *prog,
44004464
type = bpf_object__section_to_libbpf_map_type(obj, shdr_idx);
44014465
sym_sec_name = elf_sec_name(obj, elf_sec_by_idx(obj, shdr_idx));
44024466

4467+
/* arena data relocation */
4468+
if (shdr_idx == obj->efile.arena_data_shndx) {
4469+
reloc_desc->type = RELO_DATA;
4470+
reloc_desc->insn_idx = insn_idx;
4471+
reloc_desc->map_idx = obj->arena_map - obj->maps;
4472+
reloc_desc->sym_off = sym->st_value;
4473+
return 0;
4474+
}
4475+
44034476
/* generic map reference relocation */
44044477
if (type == LIBBPF_MAP_UNSPEC) {
44054478
if (!bpf_object__shndx_is_maps(obj, shdr_idx)) {
@@ -4940,6 +5013,7 @@ bpf_object__populate_internal_map(struct bpf_object *obj, struct bpf_map *map)
49405013
bpf_gen__map_freeze(obj->gen_loader, map - obj->maps);
49415014
return 0;
49425015
}
5016+
49435017
err = bpf_map_update_elem(map->fd, &zero, map->mmaped, 0);
49445018
if (err) {
49455019
err = -errno;
@@ -5289,6 +5363,10 @@ bpf_object__create_maps(struct bpf_object *obj)
52895363
map->name, err);
52905364
return err;
52915365
}
5366+
if (obj->arena_data) {
5367+
memcpy(map->mmaped, obj->arena_data, obj->arena_data_sz);
5368+
zfree(&obj->arena_data);
5369+
}
52925370
}
52935371
if (map->init_slots_sz && map->def.type != BPF_MAP_TYPE_PROG_ARRAY) {
52945372
err = init_map_in_map_slots(obj, map);
@@ -8786,13 +8864,9 @@ static void bpf_map__destroy(struct bpf_map *map)
87868864
zfree(&map->init_slots);
87878865
map->init_slots_sz = 0;
87888866

8789-
if (map->mmaped) {
8790-
size_t mmap_sz;
8791-
8792-
mmap_sz = bpf_map_mmap_sz(map);
8793-
munmap(map->mmaped, mmap_sz);
8794-
map->mmaped = NULL;
8795-
}
8867+
if (map->mmaped && map->mmaped != map->obj->arena_data)
8868+
munmap(map->mmaped, bpf_map_mmap_sz(map));
8869+
map->mmaped = NULL;
87968870

87978871
if (map->st_ops) {
87988872
zfree(&map->st_ops->data);
@@ -8852,6 +8926,8 @@ void bpf_object__close(struct bpf_object *obj)
88528926
if (obj->token_fd > 0)
88538927
close(obj->token_fd);
88548928

8929+
zfree(&obj->arena_data);
8930+
88558931
free(obj);
88568932
}
88578933

@@ -10063,18 +10139,26 @@ __u32 bpf_map__btf_value_type_id(const struct bpf_map *map)
1006310139
int bpf_map__set_initial_value(struct bpf_map *map,
1006410140
const void *data, size_t size)
1006510141
{
10142+
size_t actual_sz;
10143+
1006610144
if (map->obj->loaded || map->reused)
1006710145
return libbpf_err(-EBUSY);
1006810146

10069-
if (!map->mmaped || map->libbpf_type == LIBBPF_MAP_KCONFIG ||
10070-
size != map->def.value_size)
10147+
if (!map->mmaped || map->libbpf_type == LIBBPF_MAP_KCONFIG)
10148+
return libbpf_err(-EINVAL);
10149+
10150+
if (map->def.type == BPF_MAP_TYPE_ARENA)
10151+
actual_sz = map->obj->arena_data_sz;
10152+
else
10153+
actual_sz = map->def.value_size;
10154+
if (size != actual_sz)
1007110155
return libbpf_err(-EINVAL);
1007210156

1007310157
memcpy(map->mmaped, data, size);
1007410158
return 0;
1007510159
}
1007610160

10077-
void *bpf_map__initial_value(struct bpf_map *map, size_t *psize)
10161+
void *bpf_map__initial_value(const struct bpf_map *map, size_t *psize)
1007810162
{
1007910163
if (bpf_map__is_struct_ops(map)) {
1008010164
if (psize)
@@ -10084,7 +10168,12 @@ void *bpf_map__initial_value(struct bpf_map *map, size_t *psize)
1008410168

1008510169
if (!map->mmaped)
1008610170
return NULL;
10087-
*psize = map->def.value_size;
10171+
10172+
if (map->def.type == BPF_MAP_TYPE_ARENA)
10173+
*psize = map->obj->arena_data_sz;
10174+
else
10175+
*psize = map->def.value_size;
10176+
1008810177
return map->mmaped;
1008910178
}
1009010179

@@ -13573,6 +13662,11 @@ int bpf_object__load_skeleton(struct bpf_object_skeleton *s)
1357313662
continue;
1357413663
}
1357513664

13665+
if (map->def.type == BPF_MAP_TYPE_ARENA) {
13666+
*mmaped = map->mmaped;
13667+
continue;
13668+
}
13669+
1357613670
if (map->def.map_flags & BPF_F_RDONLY_PROG)
1357713671
prot = PROT_READ;
1357813672
else

tools/lib/bpf/libbpf.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1014,7 +1014,7 @@ LIBBPF_API int bpf_map__set_map_extra(struct bpf_map *map, __u64 map_extra);
10141014

10151015
LIBBPF_API int bpf_map__set_initial_value(struct bpf_map *map,
10161016
const void *data, size_t size);
1017-
LIBBPF_API void *bpf_map__initial_value(struct bpf_map *map, size_t *psize);
1017+
LIBBPF_API void *bpf_map__initial_value(const struct bpf_map *map, size_t *psize);
10181018

10191019
/**
10201020
* @brief **bpf_map__is_internal()** tells the caller whether or not the

0 commit comments

Comments
 (0)