Skip to content

Commit 0087a68

Browse files
anakryikoborkmann
authored andcommitted
libbpf: Automatically fix up BPF_MAP_TYPE_RINGBUF size, if necessary
Kernel imposes a pretty particular restriction on ringbuf map size. It has to be a power-of-2 multiple of page size. While generally this isn't hard for user to satisfy, sometimes it's impossible to do this declaratively in BPF source code or just plain inconvenient to do at runtime. One such example might be BPF libraries that are supposed to work on different architectures, which might not agree on what the common page size is. Let libbpf find the right size for user instead, if it turns out to not satisfy kernel requirements. If user didn't set size at all, that's most probably a mistake so don't upsize such zero size to one full page, though. Also we need to be careful about not overflowing __u32 max_entries. Signed-off-by: Andrii Nakryiko <[email protected]> Signed-off-by: Daniel Borkmann <[email protected]> Link: https://lore.kernel.org/bpf/[email protected]
1 parent f760d05 commit 0087a68

File tree

1 file changed

+41
-1
lines changed

1 file changed

+41
-1
lines changed

tools/lib/bpf/libbpf.c

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4943,6 +4943,44 @@ bpf_object__populate_internal_map(struct bpf_object *obj, struct bpf_map *map)
49434943

49444944
static void bpf_map__destroy(struct bpf_map *map);
49454945

4946+
static bool is_pow_of_2(size_t x)
4947+
{
4948+
return x && (x & (x - 1));
4949+
}
4950+
4951+
static size_t adjust_ringbuf_sz(size_t sz)
4952+
{
4953+
__u32 page_sz = sysconf(_SC_PAGE_SIZE);
4954+
__u32 i, mul;
4955+
4956+
/* if user forgot to set any size, make sure they see error */
4957+
if (sz == 0)
4958+
return 0;
4959+
/* Kernel expects BPF_MAP_TYPE_RINGBUF's max_entries to be
4960+
* a power-of-2 multiple of kernel's page size. If user diligently
4961+
* satisified these conditions, pass the size through.
4962+
*/
4963+
if ((sz % page_sz) == 0 && is_pow_of_2(sz / page_sz))
4964+
return sz;
4965+
4966+
/* Otherwise find closest (page_sz * power_of_2) product bigger than
4967+
* user-set size to satisfy both user size request and kernel
4968+
* requirements and substitute correct max_entries for map creation.
4969+
*/
4970+
for (i = 0, mul = 1; ; i++, mul <<= 1) {
4971+
if (mul > UINT_MAX / page_sz) /* prevent __u32 overflow */
4972+
break;
4973+
if (mul * page_sz > sz)
4974+
return mul * page_sz;
4975+
}
4976+
4977+
/* if it's impossible to satisfy the conditions (i.e., user size is
4978+
* very close to UINT_MAX but is not a power-of-2 multiple of
4979+
* page_size) then just return original size and let kernel reject it
4980+
*/
4981+
return sz;
4982+
}
4983+
49464984
static int bpf_object__create_map(struct bpf_object *obj, struct bpf_map *map, bool is_inner)
49474985
{
49484986
LIBBPF_OPTS(bpf_map_create_opts, create_attr);
@@ -4981,6 +5019,9 @@ static int bpf_object__create_map(struct bpf_object *obj, struct bpf_map *map, b
49815019
}
49825020

49835021
switch (def->type) {
5022+
case BPF_MAP_TYPE_RINGBUF:
5023+
map->def.max_entries = adjust_ringbuf_sz(map->def.max_entries);
5024+
/* fallthrough */
49845025
case BPF_MAP_TYPE_PERF_EVENT_ARRAY:
49855026
case BPF_MAP_TYPE_CGROUP_ARRAY:
49865027
case BPF_MAP_TYPE_STACK_TRACE:
@@ -4994,7 +5035,6 @@ static int bpf_object__create_map(struct bpf_object *obj, struct bpf_map *map, b
49945035
case BPF_MAP_TYPE_SOCKHASH:
49955036
case BPF_MAP_TYPE_QUEUE:
49965037
case BPF_MAP_TYPE_STACK:
4997-
case BPF_MAP_TYPE_RINGBUF:
49985038
create_attr.btf_fd = 0;
49995039
create_attr.btf_key_type_id = 0;
50005040
create_attr.btf_value_type_id = 0;

0 commit comments

Comments
 (0)