cpu电源管理

一、CPU电源管理简介

CPU是设备的控制核心,它的电源管理是整个SOC电源管理非常重要的一环。常见的CPU电源管理设计,主要也是围绕静态功耗和动态功耗的设计和优化展开:

1)静态功耗:ASIC集成电路的最基本单元是晶体管,我们的SOC在待机时仍然会有漏电流产生,从而引起静态功耗的消耗,得益于晶圆厂SOC制程(工艺)不断改进,如相比7nm FinFET,台积电5nm EUV工艺能效提升15%,功耗减少30%。所以从这个角度来讲,CPU要降低静态功耗,需要ASIC工程师把面积做小,同时需要晶圆厂把工艺制程不断改进。

2)动态功耗:这块比较复杂,软硬件配合的有cpufreq、cpuidle、锁机制底层实现等,ASIC方面有clock gating、不同power domain等,我们简单介绍下。

  • cpufreq:根据cpu负载,选择满足性能需求的最低的电压/频率对(也叫OPP,Operating Performance Points)。对ARM而言,这种电压/频率对的调整的最小单位是cluster,也就是说一个cluster里的所有工作的CPU核心的频率/电压对是相同的。在ARM支持DynamIQ后,不再是只有2个cluster了,每个cluster也不要求放置同一种微架构的CPU核心了,而是出现了如1小核+3中核+4小核的结构,笔者手中的Google Pixel4手机,就是这种架构,这款手机中vendor厂商把大核和中核,电压设置成一路电源去供电,而频率却不一样,这样对软件调频的策略提出了更高的要求,这里不再展开了。这种电压/频率对的调整,其他很多设备也都适用,统称为DVFS(Dynamic Voltage/Frequency Scaling,动态电压/频率调整)。

  • cpuidle:Linux用C-States来描述,最早应该是Intel提出来的概念,有4个状态(C0到C3)。ARM架构中,C0对应Running状态,C1对应Idle状态,C3对应Sleep状态,没有C2,CPU根据设定策略进入不同的低功耗状态,当然进出不同低功耗状态的时延是不也一样的。C1状态对应我们的cpuidle去控制,底层实现一般会执行WFI(Wait for Interrupt )指令,等到有中断的时候从C1迁移至C0状态,如果没有中断且满足一定条件会进一步转移至C3状态。CPU内部会有一个状态机控制这些状态的迁移和转换,不同状态可以简单的理解为参与工作的CPU部件不一样,状态的数字越小,参与工作的部件就越多。细节请参考ARM ARM(ARM Architecture Reference Manual)和各个CPU的微架构的TRM(Technical Reference Manual)。

  • spinlock锁机制:底层实现会执行WFE(Wait for Event)指令,进入低功耗状态,待收到SEV指令的时候,会从低功耗状态退出。避免在lock时忙等,节省部分功耗。

  • clock gating:这是ASIC设计的自动门控功能,一般外设会有寄存器控制是否使能,而CPU主要是在WFI的时候会关闭一些时钟,这个是CPU内部的状态机逻辑来做的,也算是clock gating,这个过程一般不需要软件参与。

  • power domain:划分不同的domain,是为了方便去做精细控制,需要软件参与。例如Pixel4手机有3个cluster划分了两个power domain(大核和中核是一个domain,小核是另外一个domain)。例如在系统轻载,甚至可以把大核和中核这两个cluster下电。细节可以参考TRM,比如A57 TRM的有关power domain划分示意如下图所示,这里不再展开。

二、cpufreq总体框架简介

介绍完CPU电源管理的常用手段,我们简单回顾下cpufreq的总体架构和各模块的作用,以及体现的软件思想。更细节的大家可以参考蜗窝科技的分析文章。

1. 主要模块介绍

  • Core:是对通用流程和方法的抽象;
  • Governor:负责调频策略,解决如何调频的问题;
  • Driver:负责平台相关调频机制的具体实现(需要直接操作硬件);
  • Stats:负责调频信息和各频点运行时间等统计,使用time_in_state ,结合算力和最大频率,可以做归一化的CPU负载统计。
  • Notifer:通知链,负责通知其他关心调频动作的driver。
  • Sysfs:暴露接口给用户态应用程序,使其可以灵活控制,实现不同的控制策略。

2. 软件思想介绍

cpufreq体现的一些软件设计思想,在Linux kernel中其他模块中也很常见,值得我们学习和借鉴:

分层思想。底层(driver)负责实现平台相关的实现,越往上越抽象(core、governor),尽量内聚(每个模块尽可能独立完成自己的功能,不依赖于模块外部的代码)。

策略/机制分离思想。内核有两种形态:

1) 内核提供机制,用户空间实现策略。比如后文我们会介绍的User Space这个governor就是由用户程序决策,直接控制频率切换策略。

2) 内核提供机制,特定的策略由governor去实现,用户空间修改governor的参数,例如后文我们介绍的schedutil如果scheduler选择用WALT去跟踪task和CPU负载就有一些参数会在用户空间调节。

观察者模式。通知链也是内核常用的设计,其他模块通过注册到通知链的回调函数,会在相应变化发生的时候调用这些回调,这些模块可以以观察者身份及时捕获这些变化。实现了解耦。

3. 调频方法和影响CPU频率的因素

从内核文档,我们可以知道:

  如果driver实现了setpolicy方法,则表明cpu硬件支持自动调频,不需要软件的governor干预,用户只要设置max/min freq(称作policy)即可。在手机上每个cluster会对应一个policy。

另外一种则需要governor实现控制策略,需要通过一定的方法计算出目标频率,告诉driver(调用driver实现的target/targetindex/fastswitch方法中的一种),最终由drvier控制CPU频率调整。

无论是采取哪种方法,一般CPU的工作频率都会和CPU工作承担的负载正相关。但也会有有一些其他因素影响CPU的频率,主要是thermal和用户态策略,thermal主要功能是一个热保护,过高的CPU温度除了引起如烫手等因素外,累积效应还有可能引起如晶体管击穿导致SOC不能正常工作,甚至会导致引起一些其他严重的后果(类似于若干年前的三星手机的电池爆炸的)。

thermal一般会通过内核态接口cpufreq_update_policy来实现频率控制。而用户态应用程序会通过sysfs去控制。最终都是影响policy的{max, min}。

4. 常见governor

  • Performance:性能优先的governor,直接将cpu频率设置为policy->{min,max}中的最大值。
  • Powersave:功耗优先的governor,直接将cpu频率设置为policy->{min,max}中的最小值。
  • Userspace:由用户空间程序通过scaling_setspeed文件节点修改频率。
  • Ondemand:根据CPU的当前使用率,动态的调节CPU频率。scheduler通过调用ondemand注册进来的钩子函数来触发系统负载的估算(异步的)。它以一定的时间间隔对系统负载情况进行采样。按需动态调整CPU频率, 如果的CPU当前使用率超过设定阈值,就会立即达到最大频率运行,等执行完毕就立即回到最低频率。好处是调频速度快,但问题是调的不够精确。
  • Conservative:类似Ondemand,不过频率调节的会平滑一下,不会有忽然调整为最大值又忽然调整为最小值的现象。区别在于:当系统CPU 负载超过一定阈值时,Conservative的目标频率会以某个步长步伐递增;当系统CPU 负载低于一定阈值时,目标频率会以某个步长步伐递减。同时也需要周期性地去计算系统负载。
  • Interactive:由Android提出的机制,未被linux kernel社区接纳,在AOSP的linux分支上存在了较长时间。它针对CPU密集的任务的调频策略会比较激进。因为它在每一个 CPU 上都注册了一个 idle notifier。当 CPU 退出 idle 时,去检查然后决策是否需要调整频率,非idle时仍然需要依赖timer去定时采样,才能知道系统负载信息。
  • schedutil:本文要讨论的重点,后续章节展开。
posted @ 2022-11-04 15:07  轻轻的吻  阅读(974)  评论(0编辑  收藏  举报