Linux static key代码编译
static __always_inline bool static_key_false(struct static_key *key)
{
return arch_static_branch(key, false);
}
static __always_inline bool arch_static_branch(struct static_key *key, bool branch)
{
asm goto("1: nop\n\t"
".pushsection __jump_table, \"aw\"\n\t"
".align 3\n\t"
".quad 1b, %l[l_yes], %c0\n\t"
".popsection\n\t"
: : "i"(&((char *)key)[branch]) : : l_yes);
return false;
l_yes:
return true;
}
对于上述代码,编译后的结果是生成一条nop指令,并将struct jump_entry结构体对应数据放到__jump_table段,其中:
code : 标号[1]对应的指令地址
target : l_yes标号对应的指令地址
key : 参数key对应的地址 | (branch)
Jump_table处理
Linux初始化的时候,在start_kernel函数中会调用jump_label_init初始化所有的__jump_table段,代码如下:
void __init jump_label_init(void)
{
//jump table段开始地址和结束地址,链接脚本中定义
struct jump_entry *iter_start = __start___jump_table;
struct jump_entry *iter_stop = __stop___jump_table;
struct static_key *key = NULL;
struct jump_entry *iter;
/*
* Since we are initializing the static_key.enabled field with
* with the 'raw' int values (to avoid pulling in atomic.h) in
* jump_label.h, let's make sure that is safe. There are only two
* cases to check since we initialize to 0 or 1.
*/
BUILD_BUG_ON((int)ATOMIC_INIT(0) != 0);
BUILD_BUG_ON((int)ATOMIC_INIT(1) != 1);
if (static_key_initialized)
return;
jump_label_lock();
jump_label_sort_entries(iter_start, iter_stop);
for (iter = iter_start; iter < iter_stop; iter++) {
struct static_key *iterk;
/* rewrite NOPs */
if (jump_label_type(iter) == JUMP_LABEL_NOP)
arch_jump_label_transform_static(iter, JUMP_LABEL_NOP); //对于arm64什么都不做
iterk = jump_entry_key(iter);
if (iterk == key)
continue;
key = iterk;
/*
* Set key->entries to iter, but preserve JUMP_LABEL_TRUE_BRANCH.
*/
//将jump_table中的jump_entry的地址记录在static_key的entries中,便于后续代码的更新--注释1
*((unsigned long *)&key->entries) += (unsigned long)iter;
#ifdef CONFIG_MODULES
key->next = NULL;
#endif
}
static_key_initialized = true;
jump_label_unlock();
}
代码更新
对于[Linux static key代码编译]中的代码,如果static key的值为false, arch_static_branch编译后代码的执行返回false,后续可以调用static_branch_inc(struct static_key *key)将static key更新为true, 以便使后续arch_static_branch编译后代码的执行返回true, 执行代码更新的代码如下:
#define static_branch_inc(x) static_key_slow_inc(&(x)->key)
void static_key_slow_inc(struct static_key *key)
{
int v, v1;
STATIC_KEY_CHECK_USE();
/*
* Careful if we get concurrent static_key_slow_inc() calls;
* later calls must wait for the first one to _finish_ the
* jump_label_update() process. At the same time, however,
* the jump_label_update() call below wants to see
* static_key_enabled(&key) for jumps to be updated properly.
*
* So give a special meaning to negative key->enabled: it sends
* static_key_slow_inc() down the slow path, and it is non-zero
* so it counts as "enabled" in jump_label_update(). Note that
* atomic_inc_unless_negative() checks >= 0, so roll our own.
*/
for (v = atomic_read(&key->enabled); v > 0; v = v1) {
v1 = atomic_cmpxchg(&key->enabled, v, v + 1);
if (likely(v1 == v))
return;
}
jump_label_lock();
if (atomic_read(&key->enabled) == 0) {
atomic_set(&key->enabled, -1);
jump_label_update(key);
atomic_set(&key->enabled, 1);
} else {
atomic_inc(&key->enabled);
}
jump_label_unlock();
}
static void jump_label_update(struct static_key *key)
{
struct jump_entry *stop = __stop___jump_table;
//根据key找到jump table中对应的jump_entry,在[Jump_table处理]中保存,参考[注释1]
struct jump_entry *entry = static_key_entries(key);
#ifdef CONFIG_MODULES
struct module *mod;
__jump_label_mod_update(key);
preempt_disable();
mod = __module_address((unsigned long)key);
if (mod)
stop = mod->jump_entries + mod->num_jump_entries;
preempt_enable();
#endif
/* if there are no users, entry can be NULL */
if (entry)
__jump_label_update(key, entry, stop);
}
static void __jump_label_update(struct static_key *key,
struct jump_entry *entry,
struct jump_entry *stop)
{
//遍历jump table中所有key等于参数key的jump entry,调用arch_jump_label_transform更新代码
for (; (entry < stop) && (jump_entry_key(entry) == key); entry++) {
/*
* entry->code set to 0 invalidates module init text sections
* kernel_text_address() verifies we are not in core kernel
* init code, see jump_label_invalidate_module_init().
*/
if (entry->code && kernel_text_address(entry->code))
arch_jump_label_transform(entry, jump_label_type(entry));
}
}
void arch_jump_label_transform(struct jump_entry *entry,
enum jump_label_type type)
{
void *addr = (void *)entry->code; //指令地址,即arch_static_branch中nop指令对应的地址
u32 insn;
if (type == JUMP_LABEL_JMP) {
//entry->target即arch_static_branch中l_yes对应的地址,insn对应的指令可以看作[b l_yes]
insn = aarch64_insn_gen_branch_imm(entry->code,
entry->target,
AARCH64_INSN_BRANCH_NOLINK);
} else {
insn = aarch64_insn_gen_nop();
}
//更新指令,下次在执行到arch_static_branch编译后的指令的时候,就返回true了
aarch64_insn_patch_text(&addr, &insn, 1);
}