• Linux内核中的static-key机制


    # Linux内核中的static-key机制

    背景

    在移植某个TP时,发现频繁操作屏幕会导致i2c总线死掉。在跟踪代码的时候,我发现了这个static-key

    因此,学习一下这块的知识。

    reference:

    介绍

    内核的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_likelystatic_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。如果没有定义的话,直接退化成likelyunlikely

    static_branch_likely static_branch_unlikely 主要是调用arch_static_brancharch_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 灵活,推荐使用。

    如果说我的文章对你有用,只不过是我站在巨人的肩膀上再继续努力罢了。
    若在页首无特别声明,本篇文章由 Schips 经过整理后发布。
    博客地址:https://www.cnblogs.com/schips/
  • 相关阅读:
    将Tomcat配置到你的mac电脑上,命令行启动tomcat
    Java反射获取字节码以及判断类型
    mysql那些事
    Hibernate 一对一中的一些问题
    Java long类型和Long类型的那些事
    java中的多线程
    Struts2:java.lang.NoSuchFieldException: resourceEntries at java.lang.Class.getDeclaredField(Class.java:1901)
    生产者-消费者模式
    并行程序设计模式--Master-Worker模式
    Ibatis的类型处理器TypeHandler解析
  • 原文地址:https://www.cnblogs.com/schips/p/the_mechanism_of_static-key_in_linux.html
Copyright © 2020-2023  润新知