• CPUFreq驱动


    CPUFreq子系统位于 drivers/cpufreq目录下,负责进行运行过程中CPU频率和电压的动态调整,即DvFS( Dynamic Voltage Frequency Scaling,动态电压频率调整)。运行时进行CPU电压和频率调整的原因是:CMOS电路中的功耗与电压的平方成正比、与频率成正比(P∝fV2)因此降低电压和频率可降低功耗。
    CPUFreq的核心层位于drivers/cpufreq/cpufreq,c下,它为各个SoC的CPUFreq驱动的实现提供了一套统一的接口,并实现了一套notifier机制,可以在 CPUFreq的策略和频率改变的时候向其他模块发出通知。另外,在CPU运行频率发生变化的时候,内核的 loops perify常数也会发生相应变化。

    SOC的CPUFreq驱动实现

    每个SoC的具体CPUFreq驱动实例只需要实现电压、频率表,以及从硬件层面完成这些变化。
    CPUFreq核心层提供了如下API以供SoC注册自身的CPUFreq驱动:
    int cpufreq_register_driver(struct cpufreq_driver *driver_data)
    其参数为一个cpufreq_driver结构体指针,实际上,cpufreq_driver封装了一个具体的SoC的CPUFreq驱动的主体,该结构体形如代码如下所示。

    struct cpufreq_driver {
    	char			name[CPUFREQ_NAME_LEN];
    	u8			flags;
    
    	/* 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);
    	int	(*target)	(struct cpufreq_policy *policy,	/* Deprecated */
    				 unsigned int target_freq,
    				 unsigned int relation);
    	int	(*target_index)	(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);
    	int	(*suspend)	(struct cpufreq_policy *policy);
    	int	(*resume)	(struct cpufreq_policy *policy);
    	struct freq_attr	**attr;
    };
    
    

    其中的 owner成员一般被设置为 THIS MODULE;name成员是CPUFreq驱动的名字,如drivers/cpufreq/s5pv210-cpufreq.c设置name为s5pv210, drivers/cpufreq/omap-cpufreq.c设置name为omap;falgs是一些暗示性的标志,譬如,若设置了 CPUFREQ_CONST_LOOPS,则是告诉内核loops_per_jiffy不会因为CPU频率的变化而变化。
    init()成员是一个per-CPU初始化函数指针,每当一个新的CPU被注册进系统的时候,该函数就被调用,该函数接受一个cpufreq_policy的指针参数,在init()成员函数中,可进行如下设置:

    policy->cpuinfo.min_freq
    policy->cpuinfo.max_freq
    

    上述代码描述的是该CPU支持的最小频率和最大频率(单位是kHz)。

    policy->cur
    

    上述代码描述的是CPU的当前频率;

    policy->policy
    policy->governor
    policy->min
    policy->max
    

    上述代码定义该CPU的缺省策略,以及在缺省策略情况下,该策略支持的最小、最大CPU频率。
    verify成员函数用于对用户的 CPUFreq策略设置进行有效性验证和数据修正。每当用户设定一个新策略时,该函数根据老的策略和新的策略,检验新策略设置的有效性并对无效设置进行必要的修正。在该成员函数的具体实现中,常用到如下辅助函数:

    static inline void
    cpufreq_verify_within_cpu_limits(struct cpufreq_policy *policy)
    {
    	cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
    	policy->cpuinfo.max_freq);
    }
    

    setpolicyo成员函数接受一个policy参数(包含policy->policypolicy->minpolicy->max等成员),实现了这个成员函数的CPU一般具备在一个范围(limit,从policy->minpolicy->max)里自动调整频率的能力。目前只有少数驱动(如intel_pstate.c和longrun.c)包含这样的成员函数,而绝大多数CPU都不会实现此函数,一般只实现target()成员函数,target()的参数直接就是一个指定的频率。
    target()成员函数用于将频率调整到一个指定的值,接受3个参数: policy、 target_freq和relation, target freq是目标频率,实际驱动总是要设定真实的CPU频率到最接近于 target_feq,并且设定的频率必须位于 policy->min到 policy->max之间。在设定频率接近 target_feq的情况下, relation若为 CPUFREQ REL I,则暗示设置的频率应该大于或等于 target_freq; relation若为 CPUFREQ_REL_H,则暗示设置的频率应该小于或等于 target_freq。
    表19.1描述了 setpolicy()和 target()所针对的CPU以及调用方式上的区别。

    setpolicy() target()
    CPU有在一定范围内独立调整频率的能力 CPU只能指定频率
    CPU freq policy 调用到setpolicy(),由CPU独立在一个范围内调整频率 由CPU Freq核心层根据系统负载和策略综合决定目标频率

    根据芯片内部PLL和分频器的关系, ARM SOC一般不具备独立调整频率的能力,往往SoC的 CPUFreq驱动会提供一个频率表,频率在该表的范围内进行变更,因此一般实现target()成员函数。
    CPUFreq核心层提供了一组与频率表相关的辅助API。

    int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy,
    				    struct cpufreq_frequency_table *table)
    {
    	unsigned int min_freq = ~0;
    	unsigned int max_freq = 0;
    	unsigned int i;
    
    	for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
    		unsigned int freq = table[i].frequency;
    		if (freq == CPUFREQ_ENTRY_INVALID) {
    			pr_debug("table entry %u is invalid, skipping
    ", i);
    
    			continue;
    		}
    		pr_debug("table entry %u: %u kHz, %u driver_data
    ",
    					i, freq, table[i].driver_data);
    		if (freq < min_freq)
    			min_freq = freq;
    		if (freq > max_freq)
    			max_freq = freq;
    	}
    
    	policy->min = policy->cpuinfo.min_freq = min_freq;
    	policy->max = policy->cpuinfo.max_freq = max_freq;
    
    	if (policy->min == ~0)
    		return -EINVAL;
    	else
    		return 0;
    }
    

    它是 cpufreq driver的init成员函数的助手,用于将policy->min和 policy->max设置为与 cpuinfo->min_freq和 cpuinfo.max_freq相同的值。

    int cpufreq_frequency_table_verify(struct cpufreq_policy *policy,
    				   struct cpufreq_frequency_table *table)
    

    它是 cpufreq driver的verify成员函数的助手,确保至少有1个有效的CPU频率位于policy->min到 policy->max的范围内。

    int cpufreq_frequency_table_target(struct cpufreq_policy *policy,
    				   struct cpufreq_frequency_table *table,
    				   unsigned int target_freq,
    				   unsigned int relation,
    				   unsigned int *index)
    

    CPUFreq的策略

    SoCCPUFreq驱动只是设定了CPU的频率参数,以及提供了设置频率的途径,但是它并不会管CPU自身究竟应该运行在哪种频率上。究竟频率依据的是哪种标准,进行何种变化而这些完全由 CPUFreq的策略( policy)决定,这些策略如表19.2所示。

    CPUFreq的策略 策略实现的方式
    cpufreq_ondemand 平时以低速的方式运行,当系统负载提高需自动提高频率
    cpufreq_performance cpu以最高频率运行,即scaling_max_freq
    cpufreq_consevative 字面含义是传统的、保守的,跟ondemand相似,区别在于动态频率在变更的时候采用渐进的方式
    cpufreq_powersave cpu以最低频率运行,即scaling_min_freq
    cpufreq_userspace 让根用户通过sys节点scaling_setspeed设置频率

    在 Android系统中,则增加了1个交互策略,该策略适合于对延迟敏感的U1交互任务,当有UI交互任务的时候,该策略会更加激进并及时地调整CPU频率。
    总而言之,系统的状态以及CPUFreq的策略共同决定了CPU频率跳变的目标, CPUFreq核心层并将目标频率传递给底层具体SoC的 CPUFreq驱动,该驱动修改硬件,完成频率的变换,如图19.2所示。

    用户空间一般可通过 /sys/devices/system/cpu/cpux/cpufreq节点来设置 CPUFreq。譬如,我们要设置 CPUFreq到700Mhz,采用 userspace策略,则运行如下命令:

    echo userspace >/sys/devices/system/cpu/cpu0/cpufreq/scaling governor
    echo 700000>/sys/devices/system/cpu/cpu/cpufreq/scaling set speed
    

    CPUFreq的性能测试和调优

    使用cpufreq-bench工具可以帮助工程师分析采用CPUFreq后对系统性能的影响;

    CPUFreq通知

    CPUFreq子系统会发出通知的情况有两种:CPUFreq的策略变化或者CPU运行频率变化。
    在策略变化的过程中,会发送3次通知:

    • CPUFREQ ADJUST:所有注册的 notifier可以根据硬件或者温度的情况去修改范围(即 policy->min和 policy->max);
    • CPUFREQ INCOMPATIBLE:除非前面的策略设定可能会导致硬件出错,否则被注册的notifier不能改变范围等设定;
    • CPUFREQ NOTIFY:所有注册的notifier都会被告知新的策略已经被设置。在频率变化的过程中,会发送2次通知;
    • CPUFREQ PRECHANGE:准备进行频率变更;
    • CPUFREQ POSTCHANGE:已经完成频率变更。

    notifier中的第3个参数是一个 cpufreq_freqs的结构体,包含cpu(CPU号)、old(过去的频率)和new(现在的频率)这3个成员。发送 CPUFREQ_PRECHANGE和 CPUFREQ_POSTCHANGE的代码如下:

    int srcu_notifier_call_chain(struct srcu_notifier_head *nh,
    		unsigned long val, void *v)
    	
    

    如果某模块关心 CPUFREQ_PRECHANGE或 CPUFREQ_POSTCHANGE事件,可简单地使用 Linux notifier机制监控。譬如, drivers/video/sallo0fbc在CPU频率变化过程中需对自身硬件进行相关设置,因此它注册了 notifier并在 CPUFREQ _PRECHANGE和CPUFREQ_POSTCHANGE情况下分别进行不同的处理,如代码清单19.3所示。

    #ifdef CONFIG_CPU_FREQ
    	fbi->freq_transition.notifier_call = sa1100fb_freq_transition;
    	fbi->freq_policy.notifier_call = sa1100fb_freq_policy;
    	cpufreq_register_notifier(&fbi->freq_transition, CPUFREQ_TRANSITION_NOTIFIER);
    	cpufreq_register_notifier(&fbi->freq_policy, CPUFREQ_POLICY_NOTIFIER);
    #endif
    
    
    /*
     * CPU clock speed change handler.  We need to adjust the LCD timing
     * parameters when the CPU clock is adjusted by the power management
     * subsystem.
     */
    static int
    sa1100fb_freq_transition(struct notifier_block *nb, unsigned long val,
    			 void *data)
    {
    	struct sa1100fb_info *fbi = TO_INF(nb, freq_transition);
    	struct cpufreq_freqs *f = data;
    	u_int pcd;
    
    	switch (val) {
    	case CPUFREQ_PRECHANGE:
    		set_ctrlr_state(fbi, C_DISABLE_CLKCHANGE);
    		break;
    
    	case CPUFREQ_POSTCHANGE:
    		pcd = get_pcd(fbi->fb.var.pixclock, f->new);
    		fbi->reg_lccr3 = (fbi->reg_lccr3 & ~0xff) | LCCR3_PixClkDiv(pcd);
    		set_ctrlr_state(fbi, C_ENABLE_CLKCHANGE);
    		break;
    	}
    	return 0;
    }
    
    

    此外,如果在系统挂起/恢复的过程中CPU频率会发生变化,则 CPUFreq子系统也会发出CPUFREQ_SUSPENDCHANGE和 CPUFREQ _RESUMECHANGE这两个通知。

    值得一提的是,除了CPU以外,一些非CPU设备也支持多个操作频率和电压,存在多个OPP。Linux3.2之后的内核也支持针对这种非CPU设备的DVFS,该套子系统为Devfreq。与CPUFreq存在一个drivers/cpufreq目录相似,在内核中也存在一个drivers/devore的目录。

  • 相关阅读:
    MQTT TLS 加密传输
    python多进程并发redis
    各种消息队列的特点
    mqtt异步publish方法
    Numpy API Analysis
    Karma install steps for unit test of Angular JS app
    reinstall bower command
    Simulate getter in JavaScript by valueOf and toString method
    How to: Raise and Consume Events
    获取对象的类型信息 (JavaScript)
  • 原文地址:https://www.cnblogs.com/linhaostudy/p/9355595.html
Copyright © 2020-2023  润新知