요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
================
Shadow Variables
================
Shadow variables are a simple way for livepatch modules to associate
additional "shadow" data with existing data structures. Shadow data is
allocated separately from parent data structures, which are left
unmodified. The shadow variable API described in this document is used
to allocate/add and remove/free shadow variables to/from their parents.
The implementation introduces a global, in-kernel hashtable that
associates pointers to parent objects and a numeric identifier of the
shadow data. The numeric identifier is a simple enumeration that may be
used to describe shadow variable version, class or type, etc. More
specifically, the parent pointer serves as the hashtable key while the
numeric id subsequently filters hashtable queries. Multiple shadow
variables may attach to the same parent object, but their numeric
identifier distinguishes between them.
1. Brief API summary
====================
(See the full API usage docbook notes in livepatch/shadow.c.)
A hashtable references all shadow variables. These references are
stored and retrieved through a <obj, id> pair.
* The klp_shadow variable data structure encapsulates both tracking
meta-data and shadow-data:
- meta-data
- obj - pointer to parent object
- id - data identifier
- data[] - storage for shadow data
It is important to note that the klp_shadow_alloc() and
klp_shadow_get_or_alloc() are zeroing the variable by default.
They also allow to call a custom constructor function when a non-zero
value is needed. Callers should provide whatever mutual exclusion
is required.
Note that the constructor is called under klp_shadow_lock spinlock. It allows
to do actions that can be done only once when a new variable is allocated.
* klp_shadow_get() - retrieve a shadow variable data pointer
- search hashtable for <obj, id> pair
* klp_shadow_alloc() - allocate and add a new shadow variable
- search hashtable for <obj, id> pair
- if exists
- WARN and return NULL
- if <obj, id> doesn't already exist
- allocate a new shadow variable
- initialize the variable using a custom constructor and data when provided
- add <obj, id> to the global hashtable
* klp_shadow_get_or_alloc() - get existing or alloc a new shadow variable
- search hashtable for <obj, id> pair
- if exists
- return existing shadow variable
- if <obj, id> doesn't already exist
- allocate a new shadow variable
- initialize the variable using a custom constructor and data when provided
- add <obj, id> pair to the global hashtable
* klp_shadow_free() - detach and free a <obj, id> shadow variable
- find and remove a <obj, id> reference from global hashtable
- if found
- call destructor function if defined
- free shadow variable
* klp_shadow_free_all() - detach and free all <_, id> shadow variables
- find and remove any <_, id> references from global hashtable
- if found
- call destructor function if defined
- free shadow variable
2. Use cases
============
(See the example shadow variable livepatch modules in samples/livepatch/
for full working demonstrations.)
For the following use-case examples, consider commit 1d147bfa6429
("mac80211: fix AP powersave TX vs. wakeup race"), which added a
spinlock to net/mac80211/sta_info.h :: struct sta_info. Each use-case
example can be considered a stand-alone livepatch implementation of this
fix.
Matching parent's lifecycle
---------------------------
If parent data structures are frequently created and destroyed, it may
be easiest to align their shadow variables lifetimes to the same
allocation and release functions. In this case, the parent data
structure is typically allocated, initialized, then registered in some
manner. Shadow variable allocation and setup can then be considered
part of the parent's initialization and should be completed before the
parent "goes live" (ie, any shadow variable get-API requests are made
for this <obj, id> pair.)
For commit 1d147bfa6429, when a parent sta_info structure is allocated,
allocate a shadow copy of the ps_lock pointer, then initialize it::
#define PS_LOCK 1
struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
const u8 *addr, gfp_t gfp)
{
struct sta_info *sta;
spinlock_t *ps_lock;
/* Parent structure is created */
sta = kzalloc(sizeof(*sta) + hw->sta_data_size, gfp);
/* Attach a corresponding shadow variable, then initialize it */
ps_lock = klp_shadow_alloc(sta, PS_LOCK, sizeof(*ps_lock), gfp,
NULL, NULL);
if (!ps_lock)
goto shadow_fail;
spin_lock_init(ps_lock);
...
When requiring a ps_lock, query the shadow variable API to retrieve one
for a specific struct sta_info:::
void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
{
spinlock_t *ps_lock;
/* sync with ieee80211_tx_h_unicast_ps_buf */
ps_lock = klp_shadow_get(sta, PS_LOCK);
if (ps_lock)
spin_lock(ps_lock);
...
When the parent sta_info structure is freed, first free the shadow
variable::
void sta_info_free(struct ieee80211_local *local, struct sta_info *sta)
{
klp_shadow_free(sta, PS_LOCK, NULL);
kfree(sta);
...
In-flight parent objects
------------------------
Sometimes it may not be convenient or possible to allocate shadow
variables alongside their parent objects. Or a livepatch fix may
require shadow variables for only a subset of parent object instances.
In these cases, the klp_shadow_get_or_alloc() call can be used to attach
shadow variables to parents already in-flight.
For commit 1d147bfa6429, a good spot to allocate a shadow spinlock is
inside ieee80211_sta_ps_deliver_wakeup()::
int ps_lock_shadow_ctor(void *obj, void *shadow_data, void *ctor_data)
{
spinlock_t *lock = shadow_data;
spin_lock_init(lock);
return 0;
}
#define PS_LOCK 1
void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
{
spinlock_t *ps_lock;
/* sync with ieee80211_tx_h_unicast_ps_buf */
ps_lock = klp_shadow_get_or_alloc(sta, PS_LOCK,
sizeof(*ps_lock), GFP_ATOMIC,
ps_lock_shadow_ctor, NULL);
if (ps_lock)
spin_lock(ps_lock);
...
This usage will create a shadow variable, only if needed, otherwise it
will use one that was already created for this <obj, id> pair.
Like the previous use-case, the shadow spinlock needs to be cleaned up.
A shadow variable can be freed just before its parent object is freed,
or even when the shadow variable itself is no longer required.
Other use-cases
---------------
Shadow variables can also be used as a flag indicating that a data
structure was allocated by new, livepatched code. In this case, it
doesn't matter what data value the shadow variable holds, its existence
suggests how to handle the parent object.
3. References
=============
* https://github.com/dynup/kpatch
The livepatch implementation is based on the kpatch version of shadow
variables.
* http://files.mkgnu.net/files/dynamos/doc/papers/dynamos_eurosys_07.pdf
Dynamic and Adaptive Updates of Non-Quiescent Subsystems in Commodity
Operating System Kernels (Kritis Makris, Kyung Dong Ryu 2007) presented
a datatype update technique called "shadow data structures".
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
Shadow variable 개요
1-20Shadow variable은 라이브패치 모듈이 기존 data structure에 별도의 추가 데이터를 연결하는 간단한 방법입니다. Shadow data는 parent structure와 따로 할당되므로 기존 구조체의 layout과 내용은 바꾸지 않습니다.
이 API는 parent에 shadow variable을 할당·추가하고, 필요가 끝나면 분리·해제합니다. 이미 실행 중인 kernel object에 새 field가 생긴 것과 비슷한 효과를 내지만, 원래 object의 ABI나 allocation size를 건드리지 않는 것이 핵심입니다.
구현은 kernel 전역 hashtable을 사용합니다. Parent object pointer가 기본 key가 되고 numeric identifier가 query를 추가로 구분합니다. 같은 parent에 여러 shadow variable을 붙일 수 있으며, id는 shadow data의 version, class, type 등을 나타내는 단순 enumeration으로 사용할 수 있습니다.
Parent pointer와 numeric id의 조합이 하나의 shadow data를 가리킵니다.
================
Shadow Variables
================
Shadow variables are a simple way for livepatch modules to associate
additional "shadow" data with existing data structures. Shadow data is
allocated separately from parent data structures, which are left
unmodified. The shadow variable API described in this document is used
to allocate/add and remove/free shadow variables to/from their parents.
The implementation introduces a global, in-kernel hashtable that
associates pointers to parent objects and a numeric identifier of the
shadow data. The numeric identifier is a simple enumeration that may be
used to describe shadow variable version, class or type, etc. More
specifically, the parent pointer serves as the hashtable key while the
numeric id subsequently filters hashtable queries. Multiple shadow
variables may attach to the same parent object, but their numeric
identifier distinguishes between them.
API 요약과 동기화 규칙
21-93전체 API 사용 설명은 `livepatch/shadow.c`의 docbook note에 있습니다. 모든 shadow variable은 하나의 hashtable에서 참조되며 `<obj, id>` pair로 저장하고 조회합니다.
`struct klp_shadow`는 tracking metadata와 실제 shadow data를 함께 담습니다. Metadata의 `obj`는 parent object pointer이고 `id`는 data identifier이며, flexible storage인 `data[]`에 shadow data가 들어갑니다.
`klp_shadow_alloc()`과 `klp_shadow_get_or_alloc()`은 기본적으로 새 variable을 0으로 초기화합니다. 0이 아닌 초기값이나 별도 초기화가 필요하면 custom constructor와 data를 전달할 수 있습니다. 호출자는 필요한 mutual exclusion을 직접 제공해야 합니다.
Constructor는 `klp_shadow_lock` spinlock을 잡은 상태에서 실행됩니다. 따라서 새로운 variable이 실제로 할당되는 단 한 번의 시점에만 해야 하는 작업을 constructor에 넣을 수 있지만, spinlock context에서 허용되는 작업만 수행해야 합니다.
`klp_shadow_get()`은 `<obj, id>`를 hashtable에서 찾아 shadow data pointer를 반환합니다.
`klp_shadow_alloc()`은 같은 pair가 이미 있으면 warning을 내고 `NULL`을 반환합니다. 없으면 새 `klp_shadow`를 할당하고, 제공된 경우 custom constructor와 data로 초기화한 다음 전역 hashtable에 추가합니다.
`klp_shadow_get_or_alloc()`은 pair가 있으면 기존 variable을 반환하고, 없을 때만 새 variable을 할당·초기화·등록합니다. 여러 경로가 이미 실행 중인 parent에 필요할 때 lazy initialization하는 용도에 맞습니다.
`klp_shadow_free()`는 특정 `<obj, id>` reference를 hashtable에서 제거합니다. 찾았다면 정의된 destructor를 호출한 뒤 variable을 해제합니다. `klp_shadow_free_all()`은 특정 id를 가진 모든 `<_, id>` entry에 같은 정리 절차를 적용합니다.
조회·생성·정리 함수의 중복 pair 처리 차이입니다.
두 allocation API는 중복 pair를 만났을 때의 정책이 다릅니다.
1. Brief API summary
====================
(See the full API usage docbook notes in livepatch/shadow.c.)
A hashtable references all shadow variables. These references are
stored and retrieved through a <obj, id> pair.
* The klp_shadow variable data structure encapsulates both tracking
meta-data and shadow-data:
- meta-data
- obj - pointer to parent object
- id - data identifier
- data[] - storage for shadow data
It is important to note that the klp_shadow_alloc() and
klp_shadow_get_or_alloc() are zeroing the variable by default.
They also allow to call a custom constructor function when a non-zero
value is needed. Callers should provide whatever mutual exclusion
is required.
Note that the constructor is called under klp_shadow_lock spinlock. It allows
to do actions that can be done only once when a new variable is allocated.
* klp_shadow_get() - retrieve a shadow variable data pointer
- search hashtable for <obj, id> pair
* klp_shadow_alloc() - allocate and add a new shadow variable
- search hashtable for <obj, id> pair
- if exists
- WARN and return NULL
- if <obj, id> doesn't already exist
- allocate a new shadow variable
- initialize the variable using a custom constructor and data when provided
- add <obj, id> to the global hashtable
* klp_shadow_get_or_alloc() - get existing or alloc a new shadow variable
- search hashtable for <obj, id> pair
- if exists
- return existing shadow variable
- if <obj, id> doesn't already exist
- allocate a new shadow variable
- initialize the variable using a custom constructor and data when provided
- add <obj, id> pair to the global hashtable
* klp_shadow_free() - detach and free a <obj, id> shadow variable
- find and remove a <obj, id> reference from global hashtable
- if found
- call destructor function if defined
- free shadow variable
* klp_shadow_free_all() - detach and free all <_, id> shadow variables
- find and remove any <_, id> references from global hashtable
- if found
- call destructor function if defined
- free shadow variable
사용 사례: lifecycle 일치와 in-flight object
94-213완전한 예제는 `samples/livepatch/`의 shadow variable 라이브패치 모듈에 있습니다. 여기서는 commit `1d147bfa6429`("mac80211: fix AP powersave TX vs. wakeup race")가 `net/mac80211/sta_info.h`의 `struct sta_info`에 spinlock을 추가한 상황을 사용합니다. 각 예시는 그 수정의 독립적인 라이브패치 구현으로 볼 수 있습니다.
Parent data structure가 자주 생성되고 파괴된다면 shadow variable lifetime을 parent의 allocation·release 함수에 맞추는 방법이 가장 단순합니다. Parent를 할당하고 초기화해 외부에 등록하기 전에 shadow allocation과 setup도 끝내야 합니다. 즉 `<obj, id>`에 대한 get 요청이 시작되기 전에 shadow가 준비되어야 합니다.
예제의 `sta_info_alloc()`은 parent `sta_info`를 `kzalloc()`한 다음 `PS_LOCK` id의 `spinlock_t` 크기로 `klp_shadow_alloc()`을 호출합니다. 실패하면 `shadow_fail`로 이동하고, 성공하면 `spin_lock_init()`으로 새 lock을 초기화합니다.
`ieee80211_sta_ps_deliver_wakeup()`은 특정 `sta_info`에 연결된 lock을 `klp_shadow_get(sta, PS_LOCK)`으로 조회합니다. Pointer가 있으면 `spin_lock()`을 호출해 `ieee80211_tx_h_unicast_ps_buf`와 동기화합니다.
Parent를 해제하는 `sta_info_free()`에서는 `kfree(sta)`보다 먼저 `klp_shadow_free(sta, PS_LOCK, NULL)`을 호출합니다. Parent pointer가 hashtable key이므로 parent memory가 사라지기 전에 연결을 제거해야 합니다.
Shadow를 parent의 초기화와 정리 순서 안에 넣습니다.
항상 parent와 함께 shadow를 만들기 어렵거나, livepatch fix가 parent instance 일부에만 shadow를 요구할 수도 있습니다. 이미 실행 중인 in-flight parent에는 `klp_shadow_get_or_alloc()`을 사용해 필요한 순간에 shadow를 붙일 수 있습니다.
예제의 `ps_lock_shadow_ctor()`는 `shadow_data`를 `spinlock_t`로 받아 `spin_lock_init()`을 수행합니다. `ieee80211_sta_ps_deliver_wakeup()`은 `GFP_ATOMIC`과 이 constructor를 넘겨 `klp_shadow_get_or_alloc()`을 호출합니다.
이 사용법은 `<obj, id>` pair에 variable이 없을 때만 생성하며, 이미 있다면 기존 lock을 사용합니다. Constructor가 allocation 시 한 번만 호출되므로 중복 초기화도 피합니다.
이전 사례와 마찬가지로 shadow spinlock은 정리해야 합니다. Parent object 해제 직전에 제거할 수도 있고, shadow 자체가 더 이상 필요하지 않은 더 이른 시점에 해제할 수도 있습니다.
Shadow variable은 새 라이브패치 code가 특정 data structure를 할당했다는 flag로도 사용할 수 있습니다. 이 경우 저장된 값보다 `<obj, id>` entry의 존재 자체가 parent object를 어떤 방식으로 처리해야 하는지 알려 줍니다.
Parent 생성 시점에 개입할 수 있는지와 필요한 instance 범위로 선택합니다.
2. Use cases
============
(See the example shadow variable livepatch modules in samples/livepatch/
for full working demonstrations.)
For the following use-case examples, consider commit 1d147bfa6429
("mac80211: fix AP powersave TX vs. wakeup race"), which added a
spinlock to net/mac80211/sta_info.h :: struct sta_info. Each use-case
example can be considered a stand-alone livepatch implementation of this
fix.
Matching parent's lifecycle
---------------------------
If parent data structures are frequently created and destroyed, it may
be easiest to align their shadow variables lifetimes to the same
allocation and release functions. In this case, the parent data
structure is typically allocated, initialized, then registered in some
manner. Shadow variable allocation and setup can then be considered
part of the parent's initialization and should be completed before the
parent "goes live" (ie, any shadow variable get-API requests are made
for this <obj, id> pair.)
For commit 1d147bfa6429, when a parent sta_info structure is allocated,
allocate a shadow copy of the ps_lock pointer, then initialize it::
#define PS_LOCK 1
struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
const u8 *addr, gfp_t gfp)
{
struct sta_info *sta;
spinlock_t *ps_lock;
/* Parent structure is created */
sta = kzalloc(sizeof(*sta) + hw->sta_data_size, gfp);
/* Attach a corresponding shadow variable, then initialize it */
ps_lock = klp_shadow_alloc(sta, PS_LOCK, sizeof(*ps_lock), gfp,
NULL, NULL);
if (!ps_lock)
goto shadow_fail;
spin_lock_init(ps_lock);
...
When requiring a ps_lock, query the shadow variable API to retrieve one
for a specific struct sta_info:::
void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
{
spinlock_t *ps_lock;
/* sync with ieee80211_tx_h_unicast_ps_buf */
ps_lock = klp_shadow_get(sta, PS_LOCK);
if (ps_lock)
spin_lock(ps_lock);
...
When the parent sta_info structure is freed, first free the shadow
variable::
void sta_info_free(struct ieee80211_local *local, struct sta_info *sta)
{
klp_shadow_free(sta, PS_LOCK, NULL);
kfree(sta);
...
In-flight parent objects
------------------------
Sometimes it may not be convenient or possible to allocate shadow
variables alongside their parent objects. Or a livepatch fix may
require shadow variables for only a subset of parent object instances.
In these cases, the klp_shadow_get_or_alloc() call can be used to attach
shadow variables to parents already in-flight.
For commit 1d147bfa6429, a good spot to allocate a shadow spinlock is
inside ieee80211_sta_ps_deliver_wakeup()::
int ps_lock_shadow_ctor(void *obj, void *shadow_data, void *ctor_data)
{
spinlock_t *lock = shadow_data;
spin_lock_init(lock);
return 0;
}
#define PS_LOCK 1
void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
{
spinlock_t *ps_lock;
/* sync with ieee80211_tx_h_unicast_ps_buf */
ps_lock = klp_shadow_get_or_alloc(sta, PS_LOCK,
sizeof(*ps_lock), GFP_ATOMIC,
ps_lock_shadow_ctor, NULL);
if (ps_lock)
spin_lock(ps_lock);
...
This usage will create a shadow variable, only if needed, otherwise it
will use one that was already created for this <obj, id> pair.
Like the previous use-case, the shadow spinlock needs to be cleaned up.
A shadow variable can be freed just before its parent object is freed,
or even when the shadow variable itself is no longer required.
Other use-cases
---------------
Shadow variables can also be used as a flag indicating that a data
structure was allocated by new, livepatched code. In this case, it
doesn't matter what data value the shadow variable holds, its existence
suggests how to handle the parent object.
참고 자료와 기원
214-226Linux livepatch의 shadow variable 구현은 GitHub의 `dynup/kpatch`에 있는 kpatch 버전을 기반으로 합니다.
이 개념의 연구 배경은 Kritis Makris와 Kyung Dong Ryu가 2007년에 발표한 "Dynamic and Adaptive Updates of Non-Quiescent Subsystems in Commodity Operating System Kernels"입니다. 이 논문은 실행을 완전히 멈출 수 없는 subsystem의 datatype을 갱신하는 기법을 `shadow data structures`라는 이름으로 제시했습니다.
현재 구현과 개념적 배경을 각각 제공하는 자료입니다.
3. References
=============
* https://github.com/dynup/kpatch
The livepatch implementation is based on the kpatch version of shadow
variables.
* http://files.mkgnu.net/files/dynamos/doc/papers/dynamos_eurosys_07.pdf
Dynamic and Adaptive Updates of Non-Quiescent Subsystems in Commodity
Operating System Kernels (Kritis Makris, Kyung Dong Ryu 2007) presented
a datatype update technique called "shadow data structures".
요약·해설
shadow-vars.rst:1-226Shadow variable은 parent object pointer와 numeric id를 key로 사용해 기존 구조체 밖에 새 data를 저장합니다. 원래 layout을 바꾸지 않아 이미 존재하는 object에도 라이브패치 상태를 추가할 수 있습니다.
새 parent의 lifecycle에 allocation·free를 맞추거나 `klp_shadow_get_or_alloc()`으로 in-flight object에 lazy attach할 수 있으며, parent가 사라지기 전에 반드시 shadow reference를 정리해야 합니다.
Constructor는 `klp_shadow_lock` 아래에서 새 allocation 때 한 번만 실행되므로 호출 context와 별도 mutual exclusion 요구를 함께 고려해야 합니다.