Linux内核的变频机制

本文专门针对linux内核的CPU主频变换机制和策略的研究和总结,针对嵌入式linux系统应用中降低功耗的有效方法, 暂时想实现的主要的机制有: cpu 级,设备驱动级,系统平台级。管理颗粒度不断递增,形成三驾马车齐驱的形势。
CPU :主要实现比较容易的在系统处于目标在于频繁发生、更高粒度的电源状态改变,主要的实现方式为 idle ,包括今天的主要想讲的动态主频。
设备驱动级 :主要实现对单个设备驱动的管理( suspend resume 等),通过系统监测将闲置的设备,通过从用户态对 sys 文件目录动态进行单个驱动设备的管理 ,置于省电模式。
系统平台级 :目标在于管理较大的、非常见的重大电源状态改变,用于减少产品设备在长时间的空闲之后,减少电源消耗   。主要实现方式是依托 linux 内核所支持的 apm 技术,实现整个系统的睡眠 / 恢复( sleep 这几个层次其实并不是相互独立的,都是相互交叉的,比如系统平台级的睡眠不可避免会涉及到 cpu sleep 模式和设备驱动的挂起,而动态主频的实现除了 cpu 本身的支持也需要外围驱动随着主频变化做出相应的适应活动。因此这里的分级只是一种粗范围的,逻辑上的分层。
前段时间还调研了一下 IBM Monta Vista 搞得那套 DPM Dynamic Power Management )机制,看了不少论文和观点,总的感觉就是太过复杂而且也不是很实用,感觉噱头大过实际功效,(因此这套机制始终还不能进入内核的 mainline ),言归正传,还是重点讲述下 cpufreq 技术。
一、为什么要cpufreq
关于要不要实现 cpufreq 技术,我也纠结过,一个原因是:当时对内核如何提供这么一套动态变频的机制还不了解,只觉得应该非常麻烦,因为涉及到外围驱动的参数更新,另外一个原因是:在 SEP4020 这种体量的处理器上跑 linux ,即使运行在最高频率时的处理能力可能也不是很富余,我再给它降频还有没有意义?挣扎之后还是觉得要实现它,我也给自己列了这么几条原因:
1. 虽然 cpu 在板级中已不是主要的耗电源,但是仍然占着举足轻重的位置,功耗机制到最后就是几毫安几毫安的扣了,降频肯定能在一定程序上节约功耗那我为什么不采用?
2. 细化功耗管理的颗粒度,为应用程序提供更多的功耗节省机制
3. 对普通的应用,系统可以运行在维持平台运作的最低频率,在有处理任务时,变频机制会自动切换到合适的高主频,并且在任务结束时重回省电的低主频,这样就解决了我之前的第二个疑惑。
  • SEP4020 在运行在 88M 时板级功耗为 :222mA
  • SEP4020 在运行在 56M 时板级功耗为 :190mA ,降低 14%
  • SEP4020 在运行在 32M 时板级功耗为 :160mA ,降低 28%
4.  实现的一些工作是我们一直需要去做但是一直没有动力做的
  • 变频会涉及到大量模块的参数的重新配置,作为 cpu 原厂,我们需要把这些参数彻底掌握
  • 对这些参数的充分理解,能对现有系统进行优化,提升整体系统的效率,比如使用发现一些参数还是太过保守 (sdram,nand), 我们的通用配置在系统降为 32M 时仍能正常工作。
5. 可行性论证没有问题:偶然看到 armkiller 同志提供的 nand 驱动代码中有变频的实现(这里非常感谢 armkiller ),网上这方面的文章很少,于是翻阅了 linux 内核源码中自带的 /documentation/cpufreq 后,对这种机制大概有一定的了解( linux 中的 documentation 是个好东东),也看到了一些处理器厂商为自己的 cpu 已经实现了的代码,如 sa1100 pxa 系列。
二、内核所提供的这种cpufreq技术的机制
1.  目的:
变频技术是指 CPU 硬件本身支持在不同的频率下运行,系统在运行过程中可以根据随时可能发生变化的系统负载情况动态在这些不同的运行频率之间进行切换,从而达到对性能和功耗做到二者兼顾的目的。
2.  来源:
虽然多个处理器生产厂家都提供了对变频技术的支持,但是其硬件实现和使用方法必然存在着细微甚至巨大的差别。这就使得每个处理器生产厂家都需要按照其特殊的硬件实现和使用方法向内核中添加代码,从而让自己产品中的变频技术在 Linux  中得到支持和使用。然而,这种内核开发模式所导致的后果是各个厂家的实现代码散落在  Linux  内核代码树的各个角落里,各种不同的实现之间没有任何代码是共享的,这给内核的维护以及将来添加对新的产品的支持都带来了巨大的开销,并直接导致了  cpufreq  内核子系统的诞生。
3.  管理策略:
Linux 内部共有五种对频率的管理策略 userspace conservative ondemand, powersave   performance
  • performance  CPU 会固定工作在其支持的最高运行频率上;
  • powersave  CPU 会固定工作在其支持的最低运行频率上。因此这两种  governors  都属于静态  governor  ,即在使用它们时  CPU  的运行频率不会根据系统运行时负载的变化动态作出调整。这两种  governors  对应的是两种极端的应用场景,使用  performance governor  体现的是对系统高性能的最大追求,而使用  powersave governor  则是对系统低功耗的最大追求。
  • Userspace :最早的  cpufreq  子系统通过  userspace governor  为用户提供了这种灵活性。系统将变频策略的决策权交给了用户态应用程序,并提供了相应的接口供用户态应用程序调节  CPU  运行频率使用。   (可以使用 Dominik  等人开发了 cpufrequtils  工具包  
  • ondemand  userspace 是内核态的检测,效率低。而 ondemand 正是人们长期以来希望看到的一个完全在内核态下工作并且能够以更加细粒度的时间间隔对系统负载情况进行采样分析的  governor
  • conservative   ondemand governor  的最初实现是在可选的频率范围内调低至下一个可用频率。这种降频策略的主导思想是尽量减小对系统性能的负面影响,从而不会使得系统性能在短时间内迅速降低以影响用户体验。但是在  ondemand governor  的这种最初实现版本在社区发布后,大量用户的使用结果表明这种担心实际上是多余的,  ondemand governor 在降频时对于目标频率的选择完全可以更加激进。因此最新的  ondemand governor  在降频时会在所有可选频率中一次性选择出可以保证  CPU  工作在  80%  以上负荷的频率,当然如果没有任何一个可选频率满足要求的话则会选择  CPU  支持的最低运行频率。大量用户的测试结果表明这种新的算法可以在不影响系统性能的前提下做到更高效的节能。在算法改进后,  ondemand governor  的名字并没有改变,而  ondemand governor  最初的实现也保存了下来,并且由于其算法的保守性而得名  conservative 
Ondemand 降频更加激进, conservative 降频比较缓慢保守,事实使用 ondemand 的效果也是比较好的。
4.  Cpufreq 在用户态所呈现的接口:
  • cpuinfo_max_freq  cpuinfo_min_freq   分别给出了  CPU  硬件所支持的最高运行频率及最低运行频率,
  • cpuinfo_cur_freq  则会从  CPU  硬件寄存器中读取  CPU  当前所处的运行频率。
  • Governor 在选择合适的运行频率时只会在  scaling_max_freq   scaling_min_freq  所确定的频率范围内进行选择
  • scaling_cur_freq  返回的是  cpufreq  模块缓存的  CPU  当前运行频率,而不会对  CPU  硬件寄存器进行检查。
  • scaling_available_governors  会告诉用户当前有哪些  governors  可供用户使用
  • scaling_driver  则会显示该  CPU  所使用的变频驱动程序
  • Scaling_governor  则会显示当前的管理策略,往这个上 echo 其他类型会有相应的转变。
  • scaling_setspeed :需将 governor 类型切换为 userspace ,才会出现,往这个文件 echo 数值,会切换主频
以下是将 governor 切换为 ondemand 后生成的 ondemand 文件夹下出现的配置文件。( conservative 就不说了,不准备使用)
  • sampling_rate :当前使用的采样间隔   ,单位:微秒
  • sampling_rate_min :允许使用的最短采样间隔
  • sampling_rate_max :允许使用的最长采样间隔
  • up_threshold  :表明了系统负载超过什么百分比时  ondemand governor  会自动提高  CPU  的运行频率
  • ignore_nice_load ignore_nice_load  文件可以设置为  0   1 是默认设置)。当这个参数设置为  1  时,任何具有  “nice” 值的处理器不计入总处理器利用率。在设置为  0  时,所有处理器都计入利用率。
  • sampling_down_factor
5.  使用方法:
cd sys/devices/system/cpu/cpu0/cpufreq/ 目录
echo 32000 > scaling_min_freq  设置最小工作频率 (khz,32000~88000)
// 若想使用 userspace 策略
# echo userspace > scaling_governor 切换工作方式为 userspace
echo 64000 > scaling_setspeed   设置成想要的工作频率 khz
// 若想使用 ondemand 策略
# echo ondemand > scaling_governor 切换工作方式为 ondemand
三、如何实现?
        首先需要干一些杂活,修改 kconfig makefile 把系统屏蔽的 cpufreq 打开,对于我们来说主要的核心有两部分:
系统相关:主要有 cpu timer (变了频率一定要更新系统 timer ,否则系统时间就不准了), sdram 等。
主要就是实现下面这个结构体:
static struct cpufreq_driver sep4020_driver =
{
       .flags      = CPUFREQ_STICKY,
       .verify     = sep4020_verify_speed,
       .target     = sep4020_target,
       .get         = sep4020_getspeed,
       .init         = sep4020_cpu_init,
       .name            = "SEP4020 Freq",
};
代码还是很简陋,很多细节都没考虑,所以具体的暂时先不讲了,大家可以先参考 pxa sa1100 的实现。
然后就是收频率影响的驱动:
简单的来说就是:系统在变化 cpu 主频的时候会调用 cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); 函数,响挂载在这个 cpu 上所有的驱动发出一个信号,驱动接收到这个信号则调用相应的处理函数。
这里把串口部分的实现简化,如下:
#ifdef CONFIG_CPU_FREQ
static int sep4020_serial_cpufreq_transition(struct notifier_block *nb, unsigned long val, void *data)
{
   //      printk("in the serial cpufreq_transition\n");
       int pmcr_pre;
       unsigned long cpu_clk,baud,baudh,baudl;
       pmcr_pre = *(volatile unsigned long*)PMU_PMCR_V;
              if(pmcr_pre > 0x4000)
              cpu_clk = (pmcr_pre-0x4000)*8000000;
       else
              cpu_clk = (pmcr_pre)*4000000;
 
       baud = cpu_clk/16/115200;      
       baudh = baud >>8;
       baudl = baud&0xff;    
 
       *(volatile unsigned char*)UART0_LCR_V |= (0x80);
       *(volatile unsigned char*)UART0_DLBL_V   = baudl;
       *(volatile unsigned char*)UART0_DLBH_V   = baudh;
       *(volatile unsigned char*)UART0_LCR_V &= ~(0x80);
       printk("in the serial cpufreq_transition\n");
    return 0;
}
static inline int sep4020_serial_cpufreq_register(void)
{
    sep4020_serial_freq_transition.notifier_call = sep4020_serial_cpufreq_transition;
 
    return cpufreq_register_notifier(&sep4020_serial_freq_transition,
                     CPUFREQ_TRANSITION_NOTIFIER);
}
 
static inline void sep4020_serial_cpufreq_deregister(void)
{
    cpufreq_unregister_notifier(&sep4020_serial_freq_transition,
                    CPUFREQ_TRANSITION_NOTIFIER);
}
 
#else
#endif
 
四、效果
sys 下开启 ondeman 模式,串上电流表:
1.       板级电流从 220mA 调至 160mA (因为此时内核检测系统无负载,降频)
2.       执行一个 nandflash 的拷贝命令,拷贝一个 5M 左右的文件到其他文件夹,
3.       在拷贝执行时间在 3 秒时(我给内核设的扫描周期为 2.5 秒)系统发现有负载,升频,电流从 160mA 变为 220mA (可见已是系统最高主频)
4.       此后的拷贝的整个过程中电流保持为 220mA
5.       在拷贝结束后不久( 2-3s 内),系统电流又跳变至 160mA

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值