2022-11-08 13:22:15 +03:00
|
|
|
.. SPDX-License-Identifier: GPL-2.0-only
|
|
|
|
.. Copyright (C) 2022 Red Hat, Inc.
|
|
|
|
|
|
|
|
========================================================
|
|
|
|
BPF_MAP_TYPE_ARRAY_OF_MAPS and BPF_MAP_TYPE_HASH_OF_MAPS
|
|
|
|
========================================================
|
|
|
|
|
|
|
|
.. note::
|
|
|
|
- ``BPF_MAP_TYPE_ARRAY_OF_MAPS`` and ``BPF_MAP_TYPE_HASH_OF_MAPS`` were
|
|
|
|
introduced in kernel version 4.12
|
|
|
|
|
|
|
|
``BPF_MAP_TYPE_ARRAY_OF_MAPS`` and ``BPF_MAP_TYPE_HASH_OF_MAPS`` provide general
|
|
|
|
purpose support for map in map storage. One level of nesting is supported, where
|
|
|
|
an outer map contains instances of a single type of inner map, for example
|
|
|
|
``array_of_maps->sock_map``.
|
|
|
|
|
|
|
|
When creating an outer map, an inner map instance is used to initialize the
|
|
|
|
metadata that the outer map holds about its inner maps. This inner map has a
|
|
|
|
separate lifetime from the outer map and can be deleted after the outer map has
|
|
|
|
been created.
|
|
|
|
|
|
|
|
The outer map supports element lookup, update and delete from user space using
|
|
|
|
the syscall API. A BPF program is only allowed to do element lookup in the outer
|
|
|
|
map.
|
|
|
|
|
|
|
|
.. note::
|
|
|
|
- Multi-level nesting is not supported.
|
|
|
|
- Any BPF map type can be used as an inner map, except for
|
|
|
|
``BPF_MAP_TYPE_PROG_ARRAY``.
|
|
|
|
- A BPF program cannot update or delete outer map entries.
|
|
|
|
|
|
|
|
For ``BPF_MAP_TYPE_ARRAY_OF_MAPS`` the key is an unsigned 32-bit integer index
|
|
|
|
into the array. The array is a fixed size with ``max_entries`` elements that are
|
|
|
|
zero initialized when created.
|
|
|
|
|
|
|
|
For ``BPF_MAP_TYPE_HASH_OF_MAPS`` the key type can be chosen when defining the
|
|
|
|
map. The kernel is responsible for allocating and freeing key/value pairs, up to
|
|
|
|
the max_entries limit that you specify. Hash maps use pre-allocation of hash
|
|
|
|
table elements by default. The ``BPF_F_NO_PREALLOC`` flag can be used to disable
|
|
|
|
pre-allocation when it is too memory expensive.
|
|
|
|
|
|
|
|
Usage
|
|
|
|
=====
|
|
|
|
|
|
|
|
Kernel BPF Helper
|
|
|
|
-----------------
|
|
|
|
|
2022-11-22 17:39:33 +03:00
|
|
|
bpf_map_lookup_elem()
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
.. code-block:: c
|
|
|
|
|
2022-11-08 13:22:15 +03:00
|
|
|
void *bpf_map_lookup_elem(struct bpf_map *map, const void *key)
|
|
|
|
|
|
|
|
Inner maps can be retrieved using the ``bpf_map_lookup_elem()`` helper. This
|
|
|
|
helper returns a pointer to the inner map, or ``NULL`` if no entry was found.
|
|
|
|
|
|
|
|
Examples
|
|
|
|
========
|
|
|
|
|
|
|
|
Kernel BPF Example
|
|
|
|
------------------
|
|
|
|
|
|
|
|
This snippet shows how to create and initialise an array of devmaps in a BPF
|
|
|
|
program. Note that the outer array can only be modified from user space using
|
|
|
|
the syscall API.
|
|
|
|
|
|
|
|
.. code-block:: c
|
|
|
|
|
|
|
|
struct inner_map {
|
|
|
|
__uint(type, BPF_MAP_TYPE_DEVMAP);
|
|
|
|
__uint(max_entries, 10);
|
|
|
|
__type(key, __u32);
|
|
|
|
__type(value, __u32);
|
|
|
|
} inner_map1 SEC(".maps"), inner_map2 SEC(".maps");
|
|
|
|
|
|
|
|
struct {
|
|
|
|
__uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
|
|
|
|
__uint(max_entries, 2);
|
|
|
|
__type(key, __u32);
|
|
|
|
__array(values, struct inner_map);
|
|
|
|
} outer_map SEC(".maps") = {
|
|
|
|
.values = { &inner_map1,
|
|
|
|
&inner_map2 }
|
|
|
|
};
|
|
|
|
|
|
|
|
See ``progs/test_btf_map_in_map.c`` in ``tools/testing/selftests/bpf`` for more
|
|
|
|
examples of declarative initialisation of outer maps.
|
|
|
|
|
|
|
|
User Space
|
|
|
|
----------
|
|
|
|
|
|
|
|
This snippet shows how to create an array based outer map:
|
|
|
|
|
|
|
|
.. code-block:: c
|
|
|
|
|
|
|
|
int create_outer_array(int inner_fd) {
|
|
|
|
LIBBPF_OPTS(bpf_map_create_opts, opts, .inner_map_fd = inner_fd);
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
fd = bpf_map_create(BPF_MAP_TYPE_ARRAY_OF_MAPS,
|
|
|
|
"example_array", /* name */
|
|
|
|
sizeof(__u32), /* key size */
|
|
|
|
sizeof(__u32), /* value size */
|
|
|
|
256, /* max entries */
|
|
|
|
&opts); /* create opts */
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
This snippet shows how to add an inner map to an outer map:
|
|
|
|
|
|
|
|
.. code-block:: c
|
|
|
|
|
|
|
|
int add_devmap(int outer_fd, int index, const char *name) {
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
fd = bpf_map_create(BPF_MAP_TYPE_DEVMAP, name,
|
|
|
|
sizeof(__u32), sizeof(__u32), 256, NULL);
|
|
|
|
if (fd < 0)
|
|
|
|
return fd;
|
|
|
|
|
|
|
|
return bpf_map_update_elem(outer_fd, &index, &fd, BPF_ANY);
|
|
|
|
}
|
|
|
|
|
|
|
|
References
|
|
|
|
==========
|
|
|
|
|
|
|
|
- https://lore.kernel.org/netdev/20170322170035.923581-3-kafai@fb.com/
|
|
|
|
- https://lore.kernel.org/netdev/20170322170035.923581-4-kafai@fb.com/
|