背景
在移植某個TP時,發現頻繁操作屏幕會導致i2c總線死掉。在跟蹤代碼的時候,我發現了這個static-key
。
因此,學習一下這塊的知識。
reference:
- https://blog.csdn.net/snoopyljc/article/details/89409645
- https://blog.csdn.net/tiantao2012/article/details/53995724
介紹
內核的static-key用來優化if-else頻繁判斷的問題.
使用
#define DEFINE_STATIC_KEY_FALSE(name) \
struct static_key_false name = STATIC_KEY_FALSE_INIT
一般使用DEFINE_STATIC_KEY_FALSE 定義條件不成立的case,用DECLARE_STATIC_KEY_TRUE定義條件成立的case
#define DEFINE_STATIC_KEY_TRUE(name) \
struct static_key_true name = STATIC_KEY_TRUE_INIT
例如kernel/sched/core.c
中的定義
DEFINE_STATIC_KEY_FALSE(sched_numa_balancing);
實際判讀的時候,可以用static_branch_likely
和static_branch_unlikely
來判斷定義的這個變量表達的條件是否成立.
if (static_branch_likely(&sched_numa_balancing))
return;
原理
首先看定義struct static_key_false name = STATIC_KEY_FALSE_INIT
static_key_false 結構體的定義如下:
#ifdef HAVE_JUMP_LABEL
struct static_key {
atomic_t enabled;
/* Set lsb bit to 1 if branch is default true, 0 ot */
struct jump_entry *entries;
#ifdef CONFIG_MODULES
struct static_key_mod *next;
#endif
};
#else
struct static_key {
atomic_t enabled;
};
#endif /* HAVE_JUMP_LABEL */
可見如果沒有定義HAVE_JUMP_LABEL
,則static_key 退化成atomic變量
#define STATIC_KEY_TRUE_INIT (struct static_key_true) { .key = STATIC_KEY_INIT_TRUE, }
#define STATIC_KEY_FALSE_INIT (struct static_key_false){ .key = STATIC_KEY_INIT_FALSE, }
#define STATIC_KEY_INIT_TRUE \
{ .enabled = { 1 }, \
.entries = (void *)JUMP_TYPE_TRUE }
#define STATIC_KEY_INIT_FALSE \
{ .enabled = { 0 }, \
.entries = (void *)JUMP_TYPE_FALSE }
//false和true的主要區別就是enabled 是否為1.
#ifdef HAVE_JUMP_LABEL
#define static_branch_likely(x) \
({ \
bool branch; \
if (__builtin_types_compatible_p(typeof(*x), struct static_key_true)) \
branch = !arch_static_branch(&(x)->key, true); \
else if (__builtin_types_compatible_p(typeof(*x), struct static_key_false)) \
branch = !arch_static_branch_jump(&(x)->key, true); \
else \
branch = ____wrong_branch_error(); \
branch; \
})
#define static_branch_unlikely(x) \
({ \
bool branch; \
if (__builtin_types_compatible_p(typeof(*x), struct static_key_true)) \
branch = arch_static_branch_jump(&(x)->key, false); \
else if (__builtin_types_compatible_p(typeof(*x), struct static_key_false)) \
branch = arch_static_branch(&(x)->key, false); \
else \
branch = ____wrong_branch_error(); \
branch; \
})
#else /* !HAVE_JUMP_LABEL */
#define static_branch_likely(x) likely(static_key_enabled(&(x)->key))
#define static_branch_unlikely(x) unlikely(static_key_enabled(&(x)->key))
#endif /* HAVE_JUMP_LABEL */
可見同樣依賴HAVE_JUMP_LABEL。如果沒有定義的話,直接退化成likely
和unlikely
static_branch_likely
和 static_branch_unlikely
主要是調用arch_static_branch
和arch_static_branch_jump
來判斷。
arch_static_branch
表示條件成立,繼續執行
例如:
if (static_branch_likely(&sched_numa_balancing))
return;
就直接return了
而arch_static_branch_jump
表示條件不成立,執行跳轉。
使用這種機制比likely和unlikely的另外一個好處就是可以動態改變執行的條件
#define static_branch_enable(x) static_key_enable(&(x)->key)
#define static_branch_disable(x) static_key_disable(&(x)->key)
調用static_key_slow_dec 來使key加1
static inline void static_key_enable(struct static_key *key)
{
int count = static_key_count(key);
WARN_ON_ONCE(count < 0 || count > 1);
if (!count)
static_key_slow_inc(key);
}
調用static_key_slow_inc 來使key減1
static inline void static_key_disable(struct static_key *key)
{
int count = static_key_count(key);
WARN_ON_ONCE(count < 0 || count > 1);
if (count)
static_key_slow_dec(key);
}
除了通過static_branch_enable和 static_branch_disable 外,還可以通過static_branch_inc 是判斷條件加1,從而是條件成立.反之依然.
#define static_branch_inc(x) static_key_slow_inc(&(x)->key)
#define static_branch_dec(x) static_key_slow_dec(&(x)->key)
總的來說static-key 機制比like/unlikely 靈活,推薦使用。