요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
.. SPDX-License-Identifier: GPL-2.0-only
.. Copyright (C) 2020 Google LLC.
===========================
BPF_MAP_TYPE_CGROUP_STORAGE
===========================
The ``BPF_MAP_TYPE_CGROUP_STORAGE`` map type represents a local fix-sized
storage. It is only available with ``CONFIG_CGROUP_BPF``, and to programs that
attach to cgroups; the programs are made available by the same Kconfig. The
storage is identified by the cgroup the program is attached to.
The map provide a local storage at the cgroup that the BPF program is attached
to. It provides a faster and simpler access than the general purpose hash
table, which performs a hash table lookups, and requires user to track live
cgroups on their own.
This document describes the usage and semantics of the
``BPF_MAP_TYPE_CGROUP_STORAGE`` map type. Some of its behaviors was changed in
Linux 5.9 and this document will describe the differences.
Usage
=====
The map uses key of type of either ``__u64 cgroup_inode_id`` or
``struct bpf_cgroup_storage_key``, declared in ``linux/bpf.h``::
struct bpf_cgroup_storage_key {
__u64 cgroup_inode_id;
__u32 attach_type;
};
``cgroup_inode_id`` is the inode id of the cgroup directory.
``attach_type`` is the program's attach type.
Linux 5.9 added support for type ``__u64 cgroup_inode_id`` as the key type.
When this key type is used, then all attach types of the particular cgroup and
map will share the same storage. Otherwise, if the type is
``struct bpf_cgroup_storage_key``, then programs of different attach types
be isolated and see different storages.
To access the storage in a program, use ``bpf_get_local_storage``::
void *bpf_get_local_storage(void *map, u64 flags)
``flags`` is reserved for future use and must be 0.
There is no implicit synchronization. Storages of ``BPF_MAP_TYPE_CGROUP_STORAGE``
can be accessed by multiple programs across different CPUs, and user should
take care of synchronization by themselves. The bpf infrastructure provides
``struct bpf_spin_lock`` to synchronize the storage. See
``tools/testing/selftests/bpf/progs/test_spin_lock.c``.
Examples
========
Usage with key type as ``struct bpf_cgroup_storage_key``::
#include <bpf/bpf.h>
struct {
__uint(type, BPF_MAP_TYPE_CGROUP_STORAGE);
__type(key, struct bpf_cgroup_storage_key);
__type(value, __u32);
} cgroup_storage SEC(".maps");
int program(struct __sk_buff *skb)
{
__u32 *ptr = bpf_get_local_storage(&cgroup_storage, 0);
__sync_fetch_and_add(ptr, 1);
return 0;
}
Userspace accessing map declared above::
#include <linux/bpf.h>
#include <linux/libbpf.h>
__u32 map_lookup(struct bpf_map *map, __u64 cgrp, enum bpf_attach_type type)
{
struct bpf_cgroup_storage_key = {
.cgroup_inode_id = cgrp,
.attach_type = type,
};
__u32 value;
bpf_map_lookup_elem(bpf_map__fd(map), &key, &value);
// error checking omitted
return value;
}
Alternatively, using just ``__u64 cgroup_inode_id`` as key type::
#include <bpf/bpf.h>
struct {
__uint(type, BPF_MAP_TYPE_CGROUP_STORAGE);
__type(key, __u64);
__type(value, __u32);
} cgroup_storage SEC(".maps");
int program(struct __sk_buff *skb)
{
__u32 *ptr = bpf_get_local_storage(&cgroup_storage, 0);
__sync_fetch_and_add(ptr, 1);
return 0;
}
And userspace::
#include <linux/bpf.h>
#include <linux/libbpf.h>
__u32 map_lookup(struct bpf_map *map, __u64 cgrp, enum bpf_attach_type type)
{
__u32 value;
bpf_map_lookup_elem(bpf_map__fd(map), &cgrp, &value);
// error checking omitted
return value;
}
Semantics
=========
``BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE`` is a variant of this map type. This
per-CPU variant will have different memory regions for each CPU for each
storage. The non-per-CPU will have the same memory region for each storage.
Prior to Linux 5.9, the lifetime of a storage is precisely per-attachment, and
for a single ``CGROUP_STORAGE`` map, there can be at most one program loaded
that uses the map. A program may be attached to multiple cgroups or have
multiple attach types, and each attach creates a fresh zeroed storage. The
storage is freed upon detach.
There is a one-to-one association between the map of each type (per-CPU and
non-per-CPU) and the BPF program during load verification time. As a result,
each map can only be used by one BPF program and each BPF program can only use
one storage map of each type. Because of map can only be used by one BPF
program, sharing of this cgroup's storage with other BPF programs were
impossible.
Since Linux 5.9, storage can be shared by multiple programs. When a program is
attached to a cgroup, the kernel would create a new storage only if the map
does not already contain an entry for the cgroup and attach type pair, or else
the old storage is reused for the new attachment. If the map is attach type
shared, then attach type is simply ignored during comparison. Storage is freed
only when either the map or the cgroup attached to is being freed. Detaching
will not directly free the storage, but it may cause the reference to the map
to reach zero and indirectly freeing all storage in the map.
The map is not associated with any BPF program, thus making sharing possible.
However, the BPF program can still only associate with one map of each type
(per-CPU and non-per-CPU). A BPF program cannot use more than one
``BPF_MAP_TYPE_CGROUP_STORAGE`` or more than one
``BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE``.
In all versions, userspace may use the attach parameters of cgroup and
attach type pair in ``struct bpf_cgroup_storage_key`` as the key to the BPF map
APIs to read or update the storage for a given attachment. For Linux 5.9
attach type shared storages, only the first value in the struct, cgroup inode
id, is used during comparison, so userspace may just specify a ``__u64``
directly.
The storage is bound at attach time. Even if the program is attached to parent
and triggers in child, the storage still belongs to the parent.
Userspace cannot create a new entry in the map or delete an existing entry.
Program test runs always use a temporary storage.
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
Cgroup local storage 개요
1-20`BPF_MAP_TYPE_CGROUP_STORAGE` 문서는 `GPL-2.0-only` license와 `Copyright (C) 2020 Google LLC.`를 명시합니다.
`BPF_MAP_TYPE_CGROUP_STORAGE` map type은 local fixed-size storage를 나타냅니다. `CONFIG_CGROUP_BPF`가 enable된 경우와 같은 Kconfig가 제공하는 cgroup attach program에서만 사용할 수 있으며, storage는 program이 attach된 cgroup으로 식별합니다.
이 map은 BPF program이 attach된 cgroup에 local storage를 제공합니다. Hash lookup을 수행하고 live cgroup을 user가 직접 추적해야 하는 general-purpose hash table보다 빠르고 단순하게 접근할 수 있습니다.
문서는 `BPF_MAP_TYPE_CGROUP_STORAGE`의 usage와 semantics를 설명하며 Linux 5.9에서 변경된 behavior를 이전 version과 비교합니다.
Key type, local storage helper와 synchronization
21-53Map key는 `linux/bpf.h`에 선언된 `__u64 cgroup_inode_id` 또는 `struct bpf_cgroup_storage_key` type을 사용합니다.
struct bpf_cgroup_storage_key {
__u64 cgroup_inode_id;
__u32 attach_type;
};
`cgroup_inode_id`는 cgroup directory의 inode ID이고 `attach_type`은 program attach type입니다.
Linux 5.9부터 `__u64 cgroup_inode_id`를 key type으로 사용할 수 있습니다. 이 type이면 특정 cgroup과 map의 모든 attach type이 같은 storage를 공유합니다. `struct bpf_cgroup_storage_key`를 사용하면 attach type이 다른 program은 서로 격리된 storage를 봅니다.
Program에서 storage에 접근하는 helper prototype은 다음과 같습니다.
void *bpf_get_local_storage(void *map, u64 flags)
`bpf_get_local_storage()`의 `flags`는 future use를 위해 reserve되어 있으며 반드시 0이어야 합니다.
Implicit synchronization은 없습니다. 여러 CPU의 여러 program이 `BPF_MAP_TYPE_CGROUP_STORAGE`에 접근할 수 있으므로 user가 직접 synchronize해야 합니다. BPF infrastructure는 `struct bpf_spin_lock`을 제공하며 example은 `tools/testing/selftests/bpf/progs/test_spin_lock.c`에 있습니다.
Structured key 사용 예제
54-91첫 kernel example은 key를 `struct bpf_cgroup_storage_key`, value를 `__u32`로 선언합니다. Program은 `bpf_get_local_storage()`로 current attachment의 storage를 얻고 `__sync_fetch_and_add()`로 값을 증가시킵니다.
#include <bpf/bpf.h>
struct {
__uint(type, BPF_MAP_TYPE_CGROUP_STORAGE);
__type(key, struct bpf_cgroup_storage_key);
__type(value, __u32);
} cgroup_storage SEC(".maps");
int program(struct __sk_buff *skb)
{
__u32 *ptr = bpf_get_local_storage(&cgroup_storage, 0);
__sync_fetch_and_add(ptr, 1);
return 0;
}
Userspace example은 cgroup inode ID와 `enum bpf_attach_type`을 `struct bpf_cgroup_storage_key`에 넣고 `bpf_map__fd()` 및 `bpf_map_lookup_elem()`으로 해당 attachment storage value를 읽습니다.
#include <linux/bpf.h>
#include <linux/libbpf.h>
__u32 map_lookup(struct bpf_map *map, __u64 cgrp, enum bpf_attach_type type)
{
struct bpf_cgroup_storage_key = {
.cgroup_inode_id = cgrp,
.attach_type = type,
};
__u32 value;
bpf_map_lookup_elem(bpf_map__fd(map), &key, &value);
// error checking omitted
return value;
}
Cgroup inode ID key 사용 예제
92-122대안은 key type으로 `__u64 cgroup_inode_id`만 사용하는 것입니다. Kernel program의 storage access 방식은 structured key example과 같습니다.
#include <bpf/bpf.h>
struct {
__uint(type, BPF_MAP_TYPE_CGROUP_STORAGE);
__type(key, __u64);
__type(value, __u32);
} cgroup_storage SEC(".maps");
int program(struct __sk_buff *skb)
{
__u32 *ptr = bpf_get_local_storage(&cgroup_storage, 0);
__sync_fetch_and_add(ptr, 1);
return 0;
}
Userspace에서는 cgroup inode ID `cgrp` 자체를 key pointer로 전달합니다. Attach type parameter는 function signature에 남아 있지만 shared storage lookup에는 사용하지 않습니다.
#include <linux/bpf.h>
#include <linux/libbpf.h>
__u32 map_lookup(struct bpf_map *map, __u64 cgrp, enum bpf_attach_type type)
{
__u32 value;
bpf_map_lookup_elem(bpf_map__fd(map), &cgrp, &value);
// error checking omitted
return value;
}
Linux 5.9 이전 semantics
123-142`BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE`는 이 map의 per-CPU variant입니다. 각 storage에 CPU별로 다른 memory region을 두며 non-per-CPU variant는 각 storage에서 같은 region을 공유합니다.
Linux 5.9 이전에는 storage lifetime이 정확히 attachment 단위였습니다. 하나의 `CGROUP_STORAGE` map은 이를 사용하는 program을 최대 하나만 load할 수 있었습니다. Program이 여러 cgroup 또는 attach type에 attach되면 각 attach가 새 zeroed storage를 만들고 detach 시 storage를 free했습니다.
Load verification 시 각 map type의 map과 BPF program 사이에 one-to-one association이 있었습니다. 따라서 각 map은 한 BPF program만, 각 program은 type별 storage map 하나만 사용할 수 있었고 다른 BPF program과 cgroup storage를 공유할 수 없었습니다.
Linux 5.9 이후 sharing과 lifetime
143-169Linux 5.9부터 storage를 여러 program이 공유할 수 있습니다. Program을 cgroup에 attach할 때 map에 같은 cgroup·attach type pair entry가 없을 때만 새 storage를 만들고, 이미 있으면 기존 storage를 새 attachment가 재사용합니다. Attach-type-shared map에서는 비교할 때 attach type을 무시합니다.
Storage는 map 또는 attached cgroup이 free될 때만 free됩니다. Detach가 storage를 직접 free하지는 않지만 map reference count를 0으로 만들면 map의 모든 storage가 간접적으로 free될 수 있습니다.
Map은 특정 BPF program과 연결되지 않으므로 sharing이 가능합니다. 하지만 BPF program 하나는 여전히 per-CPU와 non-per-CPU 각 type당 map 하나만 연결할 수 있습니다. 즉 `BPF_MAP_TYPE_CGROUP_STORAGE`나 `BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE`를 각각 둘 이상 사용할 수 없습니다.
모든 version에서 userspace는 `struct bpf_cgroup_storage_key`의 cgroup·attach type pair를 BPF map API key로 사용해 특정 attachment storage를 read하거나 update할 수 있습니다. Linux 5.9 attach-type-shared storage는 비교할 때 struct의 첫 value인 cgroup inode ID만 사용하므로 userspace가 `__u64`를 직접 지정할 수 있습니다.
Storage binding은 attach time에 결정됩니다. Program을 parent에 attach하고 child에서 trigger해도 storage는 parent에 속합니다.
Userspace는 map에 새 entry를 만들거나 기존 entry를 delete할 수 없습니다. Program test run은 항상 temporary storage를 사용합니다.
요약과 해설
map_cgroup_storage.rst:1-169Cgroup storage map은 BPF program이 attach된 cgroup에 fixed-size local storage를 제공합니다. Structured key는 attach type별 storage를 분리하고 `__u64` inode key는 attach type 사이에서 storage를 공유합니다.
Linux 5.9 이전에는 map과 program이 one-to-one이고 storage lifetime도 attachment 단위였습니다. 이후에는 여러 program이 cgroup·attach type pair의 storage를 재사용하며 map 또는 cgroup lifetime에 맞춰 free합니다.
Concurrent access에는 implicit synchronization이 없으므로 `bpf_spin_lock` 같은 mechanism이 필요합니다. Userspace는 storage entry를 임의로 생성하거나 삭제할 수 없습니다.