Linux static_key简记(Arm64)

3 篇文章 0 订阅

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);
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值