【cpufreq】linux cpufreq软件架构分析(2)

cpufreq的全局变量

首先介绍几个cpufreq的关键全局变量,能大概了解cpufreq的实现

static struct cpufreq_driver *cpufreq_driver; /* 调频驱动指针,驱动开发者根据具体hardware实现,主要包括:设置频率,获取频率等接口 */
static DEFINE_PER_CPU(struct cpufreq_policy *, cpufreq_cpu_data); /* 区分大小核时,每个CPU可有不同的调频策略 */
static DEFINE_RWLOCK(cpufreq_driver_lock); /* 这个锁保护上面的数据 */

static LIST_HEAD(cpufreq_policy_list); /* CPU策略链表指针 */
static LIST_HEAD(cpufreq_governor_list); /* governor链表指针,cpufreq支持performance、powersave等多个governor */

static BLOCKING_NOTIFIER_HEAD(cpufreq_policy_notifier_list); /* policy更新时,回调通知各注册模块 */
static struct srcu_notifier_head cpufreq_transition_notifier_list; /* 频率变化时,回调通知各注册模块 */

static bool cpufreq_suspended; /* cpufreq是否进入suspend */

struct kobject *cpufreq_global_kobject; /* cpufreq的文件节点,路径sys/devices/system/cpu/ */

struct cpufreq_policy结构体

每个CPU有自己的policy,包含的数据有:频率表、当前使用的governor等

struct cpufreq_policy {
    /* CPUs sharing clock, require sw coordination */
    cpumask_var_t       cpus;   /* Online CPUs only */
    cpumask_var_t       related_cpus; /* Online + Offline CPUs */
    cpumask_var_t       real_cpus; /* Related and present */

    unsigned int        shared_type; /* ACPI: ANY or ALL affected CPUs should set cpufreq */
    unsigned int        cpu;    /* cpu managing this policy, must be online */

    struct clk      *clk;
    struct cpufreq_cpuinfo  cpuinfo;/* see above */

    unsigned int        min;    /* in kHz */
    unsigned int        max;    /* in kHz */
    unsigned int        cur;    /* in kHz, only needed if cpufreq governors are used */
    unsigned int        restore_freq; /* = policy->cur before transition */
    unsigned int        suspend_freq; /* freq to set during suspend */

    unsigned int        policy; /* see above */
    unsigned int        last_policy; /* policy before unplug */
    struct cpufreq_governor *governor; /* 当前CPU使用的governor指针 */
    void            *governor_data; /* governor使用的一些私有配置数据,不同governor有不同的配置 */
    char            last_governor[CPUFREQ_NAME_LEN]; /* last governor used */

    struct work_struct  update; /* if update_policy() needs to be called, but you're in IRQ context */

    struct cpufreq_user_policy user_policy;
    struct cpufreq_frequency_table  *freq_table; /* 频率表,开发根据具体hardware配置 */
    enum cpufreq_table_sorting freq_table_sorted; /* 频率表是升序还是降序 */

    struct list_head        policy_list;
    struct kobject      kobj;
    struct completion   kobj_unregister;

    /*
     * The rules for this semaphore:
     * - Any routine that wants to read from the policy structure will
     *   do a down_read on this semaphore.
     * - Any routine that will write to the policy structure and/or may take away
     *   the policy altogether (eg. CPU hotplug), will hold this lock in write
     *   mode before doing so.
     */
    struct rw_semaphore rwsem;

    /*
     * Fast switch flags:
     * - fast_switch_possible should be set by the driver if it can
     *   guarantee that frequency can be changed on any CPU sharing the
     *   policy and that the change will affect all of the policy CPUs then.
     * - fast_switch_enabled is to be set by governors that support fast
     *   frequency switching with the help of cpufreq_enable_fast_switch().
     */
    /* 由调频drivers设置,如果drivers支持在任意CPU调频且共享policy的所有CPU频率都生效,那么配置为1 */
    bool            fast_switch_possible;
    /* 由governor设置,如果fast_switch_possible不支持则不支持快速调频,或者如果注册了调频通知事件,通知操作就意味着不能快速调频 */
    bool            fast_switch_enabled;

    /*
     * Preferred average time interval between consecutive invocations of
     * the driver to set the frequency for this policy.  To be set by the
     * scaling driver (0, which is the default, means no preference).
     */
    unsigned int        transition_delay_us;

   /*
     * Remote DVFS flag (Not added to the driver structure as we don't want
     * to access another structure from scheduler hotpath).
     *
     * Should be set if CPUs can do DVFS on behalf of other CPUs from
     * different cpufreq policies.
     */
    bool            dvfs_possible_from_any_cpu;

     /* Cached frequency lookup from cpufreq_driver_resolve_freq. */
     /* 最后一次目标频率值 */
    unsigned int cached_target_freq;
    int cached_resolved_idx;

    /* Synchronization for frequency transitions */
    bool            transition_ongoing; /* Tracks transition status */
    spinlock_t      transition_lock;
    wait_queue_head_t   transition_wait;
    struct task_struct  *transition_task; /* Task which is doing the transition */

    /* cpufreq-stats */
    struct cpufreq_stats    *stats;

    /* For cpufreq driver's internal use */
    void            *driver_data;
};
View Code

struct cpufreq_driver结构体

驱动开发者根据特定hardware平台来实现struct cpufreq_driver结构体的核心功能

struct cpufreq_driver {
    char        name[CPUFREQ_NAME_LEN];
    u8      flags;
    void        *driver_data;

    /* needed by all drivers */
    int     (*init)(struct cpufreq_policy *policy);
    int     (*verify)(struct cpufreq_policy *policy);

    /* define one out of two */
    int     (*setpolicy)(struct cpufreq_policy *policy);

    /*
     * On failure, should always restore frequency to policy->restore_freq
     * (i.e. old freq).
     */
    int     (*target)(struct cpufreq_policy *policy,
                  unsigned int target_freq,
                  unsigned int relation);   /* Deprecated */
    int     (*target_index)(struct cpufreq_policy *policy,
                    unsigned int index);
    unsigned int    (*fast_switch)(struct cpufreq_policy *policy,
                       unsigned int target_freq);

    /*
     * Caches and returns the lowest driver-supported frequency greater than
     * or equal to the target frequency, subject to any driver limitations.
     * Does not set the frequency. Only to be implemented for drivers with
     * target().
     */
    unsigned int    (*resolve_freq)(struct cpufreq_policy *policy,
                    unsigned int target_freq);

    /*
     * Only for drivers with target_index() and CPUFREQ_ASYNC_NOTIFICATION
     * unset.
     *
     * get_intermediate should return a stable intermediate frequency
     * platform wants to switch to and target_intermediate() should set CPU
     * to to that frequency, before jumping to the frequency corresponding
     * to 'index'. Core will take care of sending notifications and driver
     * doesn't have to handle them in target_intermediate() or
     * target_index().
     *
     * Drivers can return '0' from get_intermediate() in case they don't
     * wish to switch to intermediate frequency for some target frequency.
     * In that case core will directly call ->target_index().
     */
    unsigned int    (*get_intermediate)(struct cpufreq_policy *policy,
                        unsigned int index);
    int     (*target_intermediate)(struct cpufreq_policy *policy,
                           unsigned int index);

    /* should be defined, if possible */
    unsigned int    (*get)(unsigned int cpu);

    /* optional */
    int     (*bios_limit)(int cpu, unsigned int *limit);

    int     (*exit)(struct cpufreq_policy *policy);
    void        (*stop_cpu)(struct cpufreq_policy *policy);
    int     (*suspend)(struct cpufreq_policy *policy);
    int     (*resume)(struct cpufreq_policy *policy);

    /* Will be called after the driver is fully initialized */
    void        (*ready)(struct cpufreq_policy *policy);

    struct freq_attr **attr;

    /* platform specific boost support code */
    bool        boost_enabled;
    int     (*set_boost)(int state);
};
View Code
  • int  (*init)(struct cpufreq_policy *policy)

init是驱动开发者编写的driver的初始化入口,其重要功能是对policy变量初始化。

  1. 申请freq_table内存空间并初始化
  2. 设置CPU进入suspend时的频率
  3. 告诉policy要管理哪些CPU,如果是SMP架构,可在回调中使用cpufreq_generic_init完成大部分功能的初始化
  4. cpuinfo,该CPU硬件支持的固定信息,包括最大频率、最小频率、切换延迟,其中最大频率、最小频率可以通过frequency table推导得出。
  • int  (*verify)(struct cpufreq_policy *policy)

当上层软件需要设定一个新的policy的时候,会调用driver的verify回调函数,检查该policy是否合法。

  • 检查逻辑很简单:policy的频率范围{min,max},是否超出policy->cpuinfo的频率范围,是否超出frequency table中的频率范围。
  • 可直接使用cpufreq_generic_frequency_table_verify接口

这里为什么要检查呢?因为涉及三个层次的频率
驱动底层:根据hardware设计的几个离散频率,代表了CPU的调频能力
中间层:policy->cpuinfo中的频率范围,一般和freq_table保持一致,也可以小于table的范围,必须在初始化时给定,之后不可修改
最上层:policy->min policy->max,用户可通过sysfs修改,verify就是检查者两个参数与freq_table进行比较,看是否设置的合理

  • int (*target)(struct cpufreq_policy *policy, unsigned int target_freq, unsigned int relation) /* Deprecated */

target回调函数,不建议使用了,就不讲了。

  • int (*target_index)(struct cpufreq_policy *policy, unsigned int index)

index表示frequency table中的index。driver需要通过index,将CPU的频率设置为对应的值,同时设置对应的电压。

  • int (*setpolicy)(struct cpufreq_policy *policy)

暂时没看到使用场景

  • unsigned int (*resolve_freq)(struct cpufreq_policy *policy, unsigned int target_freq)

只是返回大于等于target_freq的驱动支持的最小频率值,不会修改频率。

  • int  (*suspend)(struct cpufreq_policy *policy)

一般使用cpufreq_generic_suspend接口,在系统进入睡眠时设置suspend频率

  • int (*resume)(struct cpufreq_policy *policy)

在系统唤醒时的执行的回调,可用于设置唤醒的频率

struct cpufreq_governor结构体

这个结构体规定了governor一些通用功能接口,可以看到接口入参均为policy,在init、start、stop、limits等会使用policy->governor_data或者policy->min等数据

struct cpufreq_governor {
    char    name[CPUFREQ_NAME_LEN];
    int (*init)(struct cpufreq_policy *policy);
    void    (*exit)(struct cpufreq_policy *policy);
    int (*start)(struct cpufreq_policy *policy);
    void    (*stop)(struct cpufreq_policy *policy);
    void    (*limits)(struct cpufreq_policy *policy);
    ssize_t (*show_setspeed)    (struct cpufreq_policy *policy, char *buf);
    int (*store_setspeed)   (struct cpufreq_policy *policy, unsigned int freq);
    /* For governors which change frequency dynamically by themselves */
    bool            dynamic_switching;
    struct list_head    governor_list;
    struct module       *owner;
};
posted @ 2023-02-05 23:24  zephyr~  阅读(372)  评论(0编辑  收藏  举报