Static Keys

 

Static Keys

Static keys는 GCC 기능과 커널 코드 교체 기술로 조건을 빠르게 수행할 수 있는 fast-path 솔루션으로 branch miss 확률을 약간 줄여준다.

 

Deprecated Method

다음과 같은 사용 방법들은 앞으로 사용되지 않는다.

  • struct static_key false = STATIC_KEY_INIT_FALSE;
  • struct static_key true = STATIC_KEY_INIT_TRUE;
  • static_key_true()
  • static_key_false()

기존 사용 예)

        struct static_key key = STATIC_KEY_INIT_FALSE;

        ...

        if (static_key_false(&key))
                do unlikely code
        else
                do likely code

        ...
        static_key_slow_inc();
        ...
        static_key_slow_dec();
        ...

 

New Method

  • DEFINE_STATIC_KEY_TRUE(key);
  • DEFINE_STATIC_KEY_FALSE(key);
  • static_branch_likely()
  • static_branch_unlikely()

개선된 사용 예)

	DEFINE_STATIC_KEY_FALSE(key);

	...

        if (static_branch_unlikely(&key))
                do unlikely code
        else
                do likely code

	...
	static_branch_enable(&key);
	...
	static_branch_disable(&key);
	...

 

Static Key 선언

STATIC_KEY_INIT

include/linux/jump_label.h

#define STATIC_KEY_INIT STATIC_KEY_INIT_FALSE

 

#define STATIC_KEY_INIT_TRUE ((struct static_key) \
                { .enabled = ATOMIC_INIT(1) })
#define STATIC_KEY_INIT_FALSE ((struct static_key) \
                { .enabled = ATOMIC_INIT(0) })

 

Static Key 브랜치

static_key_false()

include/linux/jump_label.h

static __always_inline bool static_key_false(struct static_key *key)
{
        return arch_static_branch(key);
}

 

static_key_true()

include/linux/jump_label.h

static __always_inline bool static_key_true(struct static_key *key)
{
        return !static_key_false(key);
}

 

arch_static_branch()

arch/arm/include/asm/jump_label.h

static __always_inline bool arch_static_branch(struct static_key *key)
{
        asm_volatile_goto("1:\n\t"
                 JUMP_LABEL_NOP "\n\t"
                 ".pushsection __jump_table,  \"aw\"\n\t"
                 ".word 1b, %l[l_yes], %c0\n\t"
                 ".popsection\n\t"
                 : :  "i" (key) :  : l_yes);

        return false;
l_yes:
        return true;
}

함수 호출 부분에 nop 코드를 배치하고 __jump_table 섹션에 3개의 word를 push한다. push되는 항목은 jump_entry 구조체 동일하다.

 

아래 그림은 기존 static key API에 의해 등록되는 과정을 보여준다.

static-keys-1a

아래 그림 역시 신규 static key API에 의해 등록되는 과정을 보여준다.

static-keys-2

 

구조체

static_key 구조체

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
};
  • enabled
    • static_key_slow_inc() 및 static_key_slow_dec() 함수에 의해 카운터 값이 바뀌며 이 값과 static key의 default 값의 조합에 의해 jump label 조건이 결정된다.
  • entries
    • jump_entry를 가리키고 있으며 lsb bit 0는 static key의 default 값이 설정되어 있다.
  • next

 

아래 표와 같이 type=entries[bit0], branch=unlikely(0)/likely(1)이라 할 때 패치될 instruction 코드의 값을 보여준다.

 *	enabled	type	branch	  instuction
 * -----------------------------+-----------
 *	0	0	0	| NOP
 *	0	0	1	| JMP
 *	0	1	0	| NOP
 *	0	1	1	| JMP
 *
 *	1	0	0	| JMP
 *	1	0	1	| NOP
 *	1	1	0	| JMP
 *	1	1	1	| NOP
  • 예) STATIC_KEY_INIT_FALSE, enabled=0(변경 없음), static_key_false() -> static_branch_unlikely()
    • instruction=NOP
  • 예) 위의 예에서 static_branch_enable()을 수행 후
    • instruction=JMP

 

jump_entry 구조체

arch/arm/include/asm/jump_label.h

struct jump_entry {
        jump_label_t code;
        jump_label_t target;
        jump_label_t key;
};
  • code
    • 함수 주소
  • target
    • 브랜치할 곳의 주소
  • key
    • static key 구조체를 가리키는 주소

 

typedef u32 jump_label_t;

 

static_key_mod 구조체

kernel/jump_label.c

struct static_key_mod {
        struct static_key_mod *next;
        struct jump_entry *entries;
        struct module *mod;
};

 

jump_label_type

include/linux/jump_label.h

enum jump_label_type { 
        JUMP_LABEL_DISABLE = 0,
        JUMP_LABEL_ENABLE,
};
  • JUMP_LABEL_DISABLE
    • NOP 코드로 jump_label 코드를 변경한다.
  • JUMP_LABEL_ENABLE
    • BRANCH 코드로 jump_label 코드를 변경한다.

 

참고

답글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.