요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
.. 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
-----------------
bpf_map_lookup_elem()
~~~~~~~~~~~~~~~~~~~~~
.. code-block:: c
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/
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
Outer map과 inner map 규칙
1-41`BPF_MAP_TYPE_ARRAY_OF_MAPS`와 `BPF_MAP_TYPE_HASH_OF_MAPS` 문서는 `GPL-2.0-only` 라이선스와 `Copyright (C) 2022 Red Hat, Inc.`를 명시합니다.
`BPF_MAP_TYPE_ARRAY_OF_MAPS`와 `BPF_MAP_TYPE_HASH_OF_MAPS`는 `kernel version 4.12`에 도입되었습니다.
두 map type은 general-purpose map-in-map storage를 제공합니다. Nesting은 한 level만 지원하며 outer map은 한 가지 type의 inner map instance를 담습니다. 예를 들어 `array_of_maps->sock_map` 형태로 구성할 수 있습니다.
Outer map을 만들 때 inner map instance 하나로 outer map이 inner map에 대해 보관할 metadata를 initialize합니다. 이 template inner map의 lifetime은 outer map과 별개이므로 outer map 생성 후 삭제할 수 있습니다.
Userspace는 syscall API로 outer map element를 lookup, update, delete할 수 있습니다. BPF program은 outer map에서 element lookup만 할 수 있습니다.
Multi-level nesting은 지원되지 않습니다. `BPF_MAP_TYPE_PROG_ARRAY`를 제외한 모든 BPF map type을 inner map으로 사용할 수 있으며, BPF program은 outer map entry를 update하거나 delete할 수 없습니다.
`BPF_MAP_TYPE_ARRAY_OF_MAPS`의 key는 array index인 unsigned 32-bit integer입니다. Array는 `max_entries`개의 고정 크기이며 생성할 때 zero-initialize됩니다.
`BPF_MAP_TYPE_HASH_OF_MAPS`의 key type은 map 정의 시 선택할 수 있습니다. Kernel은 지정된 `max_entries`까지 key/value pair를 할당하고 해제합니다. Hash element는 기본적으로 pre-allocation되며 memory 비용이 너무 크면 `BPF_F_NO_PREALLOC`로 끌 수 있습니다.
Kernel BPF inner map lookup
42-57BPF program에서 inner map을 가져올 때는 다음 `bpf_map_lookup_elem()` helper를 사용합니다.
void *bpf_map_lookup_elem(struct bpf_map *map, const void *key)
Helper는 key에 연결된 inner map pointer를 반환하며 entry가 없으면 `NULL`을 반환합니다.
Declarative array-of-devmaps 예제
58-89다음 BPF code는 devmap array를 만들고 initialize합니다. Outer array는 userspace의 syscall API로만 수정할 수 있습니다.
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 }
};
`inner_map1`과 `inner_map2`는 같은 `struct inner_map` schema의 `BPF_MAP_TYPE_DEVMAP`입니다. `outer_map`은 `BPF_MAP_TYPE_ARRAY_OF_MAPS`이며 `.values` initializer에서 두 inner map의 address를 연결합니다.
Outer map의 declarative initialization example은 `tools/testing/selftests/bpf`의 `progs/test_btf_map_in_map.c`에서 더 확인할 수 있습니다.
Userspace 생성·삽입과 참고 자료
90-130다음 code는 `LIBBPF_OPTS(bpf_map_create_opts, ...)`의 `inner_map_fd`에 template inner map fd를 설정한 뒤 array-based outer map을 만듭니다.
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;
}
`bpf_map_create()`에는 `BPF_MAP_TYPE_ARRAY_OF_MAPS`, map name, key/value size, max entries 256, create option을 전달합니다.
다음 code는 새 `BPF_MAP_TYPE_DEVMAP`을 만든 뒤 해당 fd를 `BPF_ANY` semantics로 outer map의 지정된 index에 추가합니다.
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);
}
Map-in-map 도입 배경과 patch discussion은 다음 자료에 있습니다.
- [https://lore.kernel.org/netdev/20170322170035.923581-3-kafai@fb.com/](https://lore.kernel.org/netdev/20170322170035.923581-3-kafai@fb.com/)
- [https://lore.kernel.org/netdev/20170322170035.923581-4-kafai@fb.com/](https://lore.kernel.org/netdev/20170322170035.923581-4-kafai@fb.com/)
요약과 해설
map_of_maps.rst:1-130Map-in-map은 outer map에서 동일한 type의 inner map을 한 단계까지 참조합니다. Outer map 생성 시 template inner map으로 schema metadata를 고정하지만 template 자체의 lifetime은 독립적입니다.
BPF program은 outer map을 lookup만 할 수 있고 update와 delete는 userspace가 수행합니다. `BPF_MAP_TYPE_PROG_ARRAY`는 inner map으로 사용할 수 없습니다.
Array outer map은 고정된 32-bit index를 사용하고 hash outer map은 key type을 선택할 수 있습니다. Userspace는 inner map fd를 outer map value로 update해 runtime 구성을 바꿉니다.