요약·해설과 원문, 전문 번역을 서로 분리했습니다. 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_SK_STORAGE
=======================
.. note::
- ``BPF_MAP_TYPE_SK_STORAGE`` was introduced in kernel version 5.2
``BPF_MAP_TYPE_SK_STORAGE`` is used to provide socket-local storage for BPF
programs. A map of type ``BPF_MAP_TYPE_SK_STORAGE`` declares the type of storage
to be provided and acts as the handle for accessing the socket-local
storage. The values for maps of type ``BPF_MAP_TYPE_SK_STORAGE`` are stored
locally with each socket instead of with the map. The kernel is responsible for
allocating storage for a socket when requested and for freeing the storage when
either the map or the socket is deleted.
.. note::
- The key type must be ``int`` and ``max_entries`` must be set to ``0``.
- The ``BPF_F_NO_PREALLOC`` flag must be used when creating a map for
socket-local storage.
Usage
=====
Kernel BPF
----------
bpf_sk_storage_get()
~~~~~~~~~~~~~~~~~~~~
.. code-block:: c
void *bpf_sk_storage_get(struct bpf_map *map, void *sk, void *value, u64 flags)
Socket-local storage for ``map`` can be retrieved from socket ``sk`` using the
``bpf_sk_storage_get()`` helper. If the ``BPF_LOCAL_STORAGE_GET_F_CREATE``
flag is used then ``bpf_sk_storage_get()`` will create the storage for ``sk``
if it does not already exist. ``value`` can be used together with
``BPF_LOCAL_STORAGE_GET_F_CREATE`` to initialize the storage value, otherwise
it will be zero initialized. Returns a pointer to the storage on success, or
``NULL`` in case of failure.
.. note::
- ``sk`` is a kernel ``struct sock`` pointer for LSM or tracing programs.
- ``sk`` is a ``struct bpf_sock`` pointer for other program types.
bpf_sk_storage_delete()
~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: c
long bpf_sk_storage_delete(struct bpf_map *map, void *sk)
Socket-local storage for ``map`` can be deleted from socket ``sk`` using the
``bpf_sk_storage_delete()`` helper. Returns ``0`` on success, or negative
error in case of failure.
User space
----------
bpf_map_update_elem()
~~~~~~~~~~~~~~~~~~~~~
.. code-block:: c
int bpf_map_update_elem(int map_fd, const void *key, const void *value, __u64 flags)
Socket-local storage for map ``map_fd`` can be added or updated locally to a
socket using the ``bpf_map_update_elem()`` libbpf function. The socket is
identified by a `socket` ``fd`` stored in the pointer ``key``. The pointer
``value`` has the data to be added or updated to the socket ``fd``. The type
and size of ``value`` should be the same as the value type of the map
definition.
The ``flags`` parameter can be used to control the update behaviour:
- ``BPF_ANY`` will create storage for `socket` ``fd`` or update existing storage.
- ``BPF_NOEXIST`` will create storage for `socket` ``fd`` only if it did not
already exist, otherwise the call will fail with ``-EEXIST``.
- ``BPF_EXIST`` will update existing storage for `socket` ``fd`` if it already
exists, otherwise the call will fail with ``-ENOENT``.
Returns ``0`` on success, or negative error in case of failure.
bpf_map_lookup_elem()
~~~~~~~~~~~~~~~~~~~~~
.. code-block:: c
int bpf_map_lookup_elem(int map_fd, const void *key, void *value)
Socket-local storage for map ``map_fd`` can be retrieved from a socket using
the ``bpf_map_lookup_elem()`` libbpf function. The storage is retrieved from
the socket identified by a `socket` ``fd`` stored in the pointer
``key``. Returns ``0`` on success, or negative error in case of failure.
bpf_map_delete_elem()
~~~~~~~~~~~~~~~~~~~~~
.. code-block:: c
int bpf_map_delete_elem(int map_fd, const void *key)
Socket-local storage for map ``map_fd`` can be deleted from a socket using the
``bpf_map_delete_elem()`` libbpf function. The storage is deleted from the
socket identified by a `socket` ``fd`` stored in the pointer ``key``. Returns
``0`` on success, or negative error in case of failure.
Examples
========
Kernel BPF
----------
This snippet shows how to declare socket-local storage in a BPF program:
.. code-block:: c
struct {
__uint(type, BPF_MAP_TYPE_SK_STORAGE);
__uint(map_flags, BPF_F_NO_PREALLOC);
__type(key, int);
__type(value, struct my_storage);
} socket_storage SEC(".maps");
This snippet shows how to retrieve socket-local storage in a BPF program:
.. code-block:: c
SEC("sockops")
int _sockops(struct bpf_sock_ops *ctx)
{
struct my_storage *storage;
struct bpf_sock *sk;
sk = ctx->sk;
if (!sk)
return 1;
storage = bpf_sk_storage_get(&socket_storage, sk, 0,
BPF_LOCAL_STORAGE_GET_F_CREATE);
if (!storage)
return 1;
/* Use 'storage' here */
return 1;
}
Please see the ``tools/testing/selftests/bpf`` directory for functional
examples.
References
==========
https://lwn.net/ml/netdev/[email protected]/
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
Socket-local storage lifetime과 map 설정
1-23`BPF_MAP_TYPE_SK_STORAGE` 문서는 `GPL-2.0-only` 라이선스와 `Copyright (C) 2022 Red Hat, Inc.`를 명시합니다.
`BPF_MAP_TYPE_SK_STORAGE`는 `kernel version 5.2`에 도입되었습니다.
`BPF_MAP_TYPE_SK_STORAGE`는 BPF program에 socket-local storage를 제공합니다. 이 type의 map은 제공할 storage type을 선언하고 socket-local storage에 접근하는 handle 역할을 합니다.
Map value는 map 자체가 아니라 각 socket에 local하게 저장됩니다. Kernel은 요청 시 socket storage를 할당하고 map 또는 socket이 삭제될 때 storage를 해제합니다.
Key type은 반드시 `int`, `max_entries`는 반드시 0이어야 합니다. Socket-local storage map을 만들 때는 `BPF_F_NO_PREALLOC` flag를 사용해야 합니다.
Kernel BPF storage get과 delete
24-59Socket `sk`에서 `map`의 socket-local storage를 가져올 때는 다음 `bpf_sk_storage_get()` helper를 사용합니다.
void *bpf_sk_storage_get(struct bpf_map *map, void *sk, void *value, u64 flags)
`BPF_LOCAL_STORAGE_GET_F_CREATE` flag를 지정하면 storage가 없을 때 새로 만듭니다. 이 flag와 함께 `value`를 전달하면 initial value로 사용하며, value가 없으면 zero-initialize됩니다. 성공하면 storage pointer, 실패하면 `NULL`을 반환합니다.
LSM 또는 tracing program에서 `sk`는 kernel `struct sock` pointer입니다. 그 밖의 program type에서는 `struct bpf_sock` pointer입니다.
Socket `sk`에서 `map`의 local storage를 삭제할 때는 다음 `bpf_sk_storage_delete()` helper를 사용합니다.
long bpf_sk_storage_delete(struct bpf_map *map, void *sk)
Delete는 성공하면 0, 실패하면 negative error를 반환합니다.
Userspace update, lookup, delete
60-110Userspace에서 socket의 local storage를 추가하거나 갱신할 때는 다음 libbpf `bpf_map_update_elem()` function을 사용합니다.
int bpf_map_update_elem(int map_fd, const void *key, const void *value, __u64 flags)
Socket은 `key` pointer에 저장한 socket `fd`로 식별합니다. `value` pointer는 해당 socket에 추가하거나 갱신할 data를 가리키며 type과 size는 map definition의 value type과 같아야 합니다.
`flags` parameter는 update behavior를 다음과 같이 제어합니다.
- `BPF_ANY`: socket fd에 storage를 만들거나 existing storage를 갱신합니다.
- `BPF_NOEXIST`: storage가 없을 때만 만들며 이미 있으면 `-EEXIST`로 실패합니다.
- `BPF_EXIST`: storage가 있을 때만 갱신하며 없으면 `-ENOENT`로 실패합니다.
Update는 성공하면 0, 실패하면 negative error를 반환합니다.
Userspace에서 socket-local storage를 가져올 때는 다음 `bpf_map_lookup_elem()` function을 사용합니다.
int bpf_map_lookup_elem(int map_fd, const void *key, void *value)
`key` pointer의 socket fd로 socket을 식별합니다. Lookup은 성공하면 0, 실패하면 negative error를 반환합니다.
Userspace에서 socket-local storage를 삭제할 때는 다음 `bpf_map_delete_elem()` function을 사용합니다.
int bpf_map_delete_elem(int map_fd, const void *key)
`key` pointer의 socket fd가 삭제 대상 socket을 식별합니다. Delete는 성공하면 0, 실패하면 negative error를 반환합니다.
Map 선언과 sockops lookup 예제
111-159다음 code는 `BPF_F_NO_PREALLOC`, `int` key, `struct my_storage` value를 사용하는 `BPF_MAP_TYPE_SK_STORAGE` map을 선언합니다.
struct {
__uint(type, BPF_MAP_TYPE_SK_STORAGE);
__uint(map_flags, BPF_F_NO_PREALLOC);
__type(key, int);
__type(value, struct my_storage);
} socket_storage SEC(".maps");
다음 `sockops` program은 context에서 `struct bpf_sock`을 얻은 뒤 socket storage를 lookup하거나 생성합니다.
SEC("sockops")
int _sockops(struct bpf_sock_ops *ctx)
{
struct my_storage *storage;
struct bpf_sock *sk;
sk = ctx->sk;
if (!sk)
return 1;
storage = bpf_sk_storage_get(&socket_storage, sk, 0,
BPF_LOCAL_STORAGE_GET_F_CREATE);
if (!storage)
return 1;
/* Use 'storage' here */
return 1;
}
`ctx->sk` 또는 `bpf_sk_storage_get()` 결과가 NULL이면 즉시 반환합니다. 유효한 storage를 얻으면 program-specific data를 그 위치에서 사용할 수 있습니다.
Functional example은 `tools/testing/selftests/bpf` directory에서 확인할 수 있습니다.
[Socket-local storage patch discussion](https://lwn.net/ml/netdev/[email protected]/)에서 도입 배경을 확인할 수 있습니다.
요약과 해설
map_sk_storage.rst:1-159`BPF_MAP_TYPE_SK_STORAGE`는 value를 map이 아닌 각 socket에 연결합니다. Kernel은 요청 시 storage를 만들고 socket이나 map이 사라질 때 자동으로 해제합니다.
Map key는 `int`, `max_entries`는 0이어야 하며 `BPF_F_NO_PREALLOC`이 필수입니다. Kernel program에서는 socket pointer를, userspace에서는 socket fd를 key로 사용합니다.
`BPF_LOCAL_STORAGE_GET_F_CREATE`는 lookup과 allocation을 결합합니다. Initial value를 전달하지 않으면 새 storage가 zero-initialize되므로 반환 pointer와 초기화 semantics를 함께 확인해야 합니다.