影子變數¶
影子變數是 livepatch 模組將額外的“影子”資料與現有資料結構關聯起來的一種簡單方法。 影子資料與父資料結構分開分配,父資料結構保持不變。 本文件中描述的影子變數 API 用於分配/新增以及移除/釋放父物件的影子變數。
該實現引入了一個全域性的核心雜湊表,它將指向父物件的指標和影子資料的數字識別符號關聯起來。 數字識別符號是一個簡單的列舉,可用於描述影子變數的版本、類或型別等。更具體地說,父指標充當雜湊表鍵,而數字 ID 隨後過濾雜湊表查詢。 多個影子變數可以附加到同一個父物件,但它們的數字識別符號將它們區分開來。
1. 簡要 API 概述¶
(請參閱 livepatch/shadow.c 中的完整 API 使用文件。)
雜湊表引用所有影子變數。 這些引用透過 <obj, id> 對進行儲存和檢索。
klp_shadow 變數資料結構封裝了跟蹤元資料和影子資料
元資料
obj - 指向父物件的指標
id - 資料識別符號
data[] - 影子資料的儲存
重要的是要注意,klp_shadow_alloc() 和 klp_shadow_get_or_alloc() 預設情況下將變數清零。它們還允許在需要非零值時呼叫自定義建構函式。呼叫者應提供所需的任何互斥量。
請注意,建構函式是在 klp_shadow_lock 自旋鎖下呼叫的。 它允許執行只能在新變數分配時執行一次的操作。
klp_shadow_get()- 檢索影子變數資料指標 - 搜尋 <obj, id> 對的雜湊表klp_shadow_alloc()- 分配並新增新的影子變數 - 搜尋 <obj, id> 對的雜湊表如果存在
發出警告並返回 NULL
如果 <obj, id> 尚不存在
分配一個新的影子變數
當提供自定義建構函式和資料時,使用它們初始化變數
將 <obj, id> 新增到全域性雜湊表
klp_shadow_get_or_alloc()- 獲取現有影子變數或分配新的影子變數 - 搜尋 <obj, id> 對的雜湊表如果存在
返回現有影子變數
如果 <obj, id> 尚不存在
分配一個新的影子變數
當提供自定義建構函式和資料時,使用它們初始化變數
將 <obj, id> 對新增到全域性雜湊表
klp_shadow_free()- 分離並釋放 <obj, id> 影子變數 - 從全域性雜湊表中查詢並移除 <obj, id> 引用如果找到
如果定義了解構函式,則呼叫解構函式
釋放影子變數
klp_shadow_free_all()- 分離並釋放所有 <_, id> 影子變數 - 從全域性雜湊表中查詢並移除任何 <_, id> 引用如果找到
如果定義了解構函式,則呼叫解構函式
釋放影子變數
2. 用例¶
(有關完整的演示,請參閱 samples/livepatch/ 中的影子變數 livepatch 模組示例。)
對於以下用例示例,請考慮 commit 1d147bfa6429 (“mac80211: fix AP powersave TX vs. wakeup race”),它向 net/mac80211/sta_info.h :: struct sta_info 添加了一個自旋鎖。 每個用例示例都可以被認為是此修復程式的獨立 livepatch 實現。
匹配父物件的生命週期¶
如果頻繁建立和銷燬父資料結構,則將影子變數的生命週期與其相同的分配和釋放函式對齊可能是最容易的。 在這種情況下,通常分配、初始化父資料結構,然後以某種方式註冊。 然後,影子變數分配和設定可以被認為是父物件初始化的一部分,並且應在父物件“上線”之前完成(即,為此 <obj, id> 對發出任何影子變數 get-API 請求。)
對於 commit 1d147bfa6429,當分配父 sta_info 結構時,分配 ps_lock 指標的影子副本,然後初始化它
#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);
...
當需要 ps_lock 時,查詢影子變數 API 以檢索特定 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);
...
當釋放父 sta_info 結構時,首先釋放影子變數
void sta_info_free(struct ieee80211_local *local, struct sta_info *sta)
{
klp_shadow_free(sta, PS_LOCK, NULL);
kfree(sta);
...
正在執行的父物件¶
有時,與父物件一起分配影子變數可能不方便或不可能。 或者,livepatch 修復可能僅需要父物件例項子集的影子變數。 在這些情況下,可以使用 klp_shadow_get_or_alloc() 呼叫將影子變數附加到已經在執行的父物件。
對於 commit 1d147bfa6429,分配影子自旋鎖的一個好位置是在 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);
...
此用法將建立一個影子變數(如果需要),否則它將使用已經為此 <obj, id> 對建立的變數。
與之前的用例類似,需要清理影子自旋鎖。 可以在釋放其父物件之前,甚至在不再需要影子變數本身時釋放影子變數。
其他用例¶
影子變數也可以用作標誌,指示資料結構是由新的、經過 livepatch 的程式碼分配的。 在這種情況下,影子變數儲存什麼資料值並不重要,它的存在表明如何處理父物件。
3. 參考¶
https://github.com/dynup/kpatch
livepatch 實現基於 kpatch 版本的影子變數。
http://files.mkgnu.net/files/dynamos/doc/papers/dynamos_eurosys_07.pdf
商品作業系統核心中非靜態子系統的動態和自適應更新(Kritis Makris,Kyung Dong Ryu 2007)提出了一種稱為“影子資料結構”的資料型別更新技術。