.. SPDX-License-Identifier: GPL-2.0 .. include:: ../disclaimer-zh_CN.rst :Original: Documentation/power/energy-model.rst :翻译: å”艺舟 Tang Yizhou <tangyeechou@gmail.com> ============ 设备能é‡æ¨¡åž‹ ============ 1. 概述 ------- 能é‡æ¨¡åž‹ï¼ˆEM)框架是一ç§é©±åŠ¨ç¨‹åºä¸Žå†…æ ¸å系统之间的接å£ã€‚å…¶ä¸é©±åŠ¨ç¨‹åºäº†è§£ä¸åŒ æ€§èƒ½å±‚çº§çš„è®¾å¤‡æ‰€æ¶ˆè€—çš„åŠŸçŽ‡ï¼Œè€Œå†…æ ¸å系统愿æ„使用该信æ¯åšå‡ºèƒ½é‡æ„ŸçŸ¥å†³ç–。 设备所消耗的功率的信æ¯æ¥æºåœ¨ä¸åŒçš„å¹³å°ä¸Šå¯èƒ½æœ‰å¾ˆå¤§çš„ä¸åŒã€‚这些功率æˆæœ¬åœ¨æŸäº› 情况下å¯ä»¥ä½¿ç”¨è®¾å¤‡æ ‘æ•°æ®æ¥ä¼°ç®—。在其它情况下,固件会更清楚。或者,用户空间å¯èƒ½ 是最清楚的。以æ¤ç±»æŽ¨ã€‚为了é¿å…æ¯ä¸€ä¸ªå®¢æˆ·ç«¯å系统对æ¯ä¸€ç§å¯èƒ½çš„ä¿¡æ¯æºè‡ªå·±é‡æ–° 实现支æŒï¼ŒEMæ¡†æž¶ä½œä¸ºä¸€ä¸ªæŠ½è±¡å±‚ä»‹å…¥ï¼Œå®ƒåœ¨å†…æ ¸ä¸å¯¹åŠŸçŽ‡æˆæœ¬è¡¨çš„æ ¼å¼è¿›è¡Œæ ‡å‡†åŒ–, å› æ¤èƒ½å¤Ÿé¿å…多余的工作。 功率值å¯ä»¥ç”¨å¾®ç“¦æˆ–“抽象刻度â€è¡¨ç¤ºã€‚多个å系统å¯èƒ½ä½¿ç”¨EM,由系统集æˆå•†æ¥æ£€æŸ¥ 功率值刻度类型的è¦æ±‚是å¦æ»¡è¶³ã€‚å¯ä»¥åœ¨èƒ½é‡æ„ŸçŸ¥è°ƒåº¦å™¨çš„文档ä¸æ‰¾åˆ°ä¸€ä¸ªä¾‹å Documentation/scheduler/sched-energy.rst。对于一些å系统,比如çƒèƒ½æˆ– powercap,用“抽象刻度â€æ述功率值å¯èƒ½ä¼šå¯¼è‡´é—®é¢˜ã€‚这些å系统对过去使用的功率的 ä¼°ç®—å€¼æ›´æ„Ÿå…´è¶£ï¼Œå› æ¤å¯èƒ½éœ€è¦çœŸå®žçš„微瓦。这些è¦æ±‚的一个例åå¯ä»¥åœ¨æ™ºèƒ½åŠŸçŽ‡åˆ†é… Documentation/driver-api/thermal/power_allocator.rst文档ä¸æ‰¾åˆ°ã€‚ å†…æ ¸å系统å¯èƒ½ï¼ˆåŸºäºŽEMå†…éƒ¨æ ‡å¿—ä½ï¼‰å®žçŽ°äº†å¯¹EM注册设备是å¦å…·æœ‰ä¸ä¸€è‡´åˆ»åº¦çš„自动 检查。è¦è®°ä½çš„é‡è¦äº‹æƒ…是,当功率值以“抽象刻度â€è¡¨ç¤ºæ—¶ï¼Œä»Žä¸æŽ¨å¯¼ä»¥å¾®ç„¦è€³ä¸ºå•ä½ 的真实能é‡æ¶ˆè€—是ä¸å¯èƒ½çš„。 下图æ述了一个驱动的例å(这里是针对Arm的,但该方法适用于任何体系结构),它 å‘EM框架æ供了功率æˆæœ¬ï¼Œæ„Ÿå…´è¶£çš„客户端å¯ä»Žä¸è¯»å–æ•°æ®:: +---------------+ +-----------------+ +---------------+ | Thermal (IPA) | | Scheduler (EAS) | | Other | +---------------+ +-----------------+ +---------------+ | | em_cpu_energy() | | | em_cpu_get() | +---------+ | +---------+ | | | v v v +---------------------+ | Energy Model | | Framework | +---------------------+ ^ ^ ^ | | | em_dev_register_perf_domain() +----------+ | +---------+ | | | +---------------+ +---------------+ +--------------+ | cpufreq-dt | | arm_scmi | | Other | +---------------+ +---------------+ +--------------+ ^ ^ ^ | | | +--------------+ +---------------+ +--------------+ | Device Tree | | Firmware | | ? | +--------------+ +---------------+ +--------------+ 对于CPU设备,EM框架管ç†ç€ç³»ç»Ÿä¸æ¯ä¸ªâ€œæ€§èƒ½åŸŸâ€çš„功率æˆæœ¬è¡¨ã€‚一个性能域是一组 性能一起伸缩的CPU。性能域通常与CPUFreqç–略具有1对1æ˜ å°„ã€‚ä¸€ä¸ªæ€§èƒ½åŸŸä¸çš„ 所有CPUè¦æ±‚具有相åŒçš„微架构。ä¸åŒæ€§èƒ½åŸŸä¸çš„CPUå¯ä»¥æœ‰ä¸åŒçš„微架构。 2. æ ¸å¿ƒAPI ---------- 2.1 é…置选项 ^^^^^^^^^^^^ 必须使能CONFIG_ENERGY_MODELæ‰èƒ½ä½¿ç”¨EM框架。 2.2 性能域的注册 ^^^^^^^^^^^^^^^^ “高级â€EM的注册 ~~~~~~~~~~~~~~~~ “高级â€EMå› å®ƒå…许驱动æ供更精确的功率模型而得å。它并ä¸å—é™äºŽæ¡†æž¶ä¸çš„一些已 实现的数å¦å…¬å¼ï¼ˆå°±åƒâ€œç®€å•â€EMé‚£æ ·ï¼‰ã€‚å®ƒå¯ä»¥æ›´å¥½åœ°åæ˜ æ¯ä¸ªæ€§èƒ½çŠ¶æ€çš„实际功率 测é‡ã€‚å› æ¤ï¼Œåœ¨EMé™æ€åŠŸçŽ‡ï¼ˆæ¼ç”µæµåŠŸçŽ‡ï¼‰æ˜¯é‡è¦çš„情况下,应该首选这ç§æ³¨å†Œæ–¹å¼ã€‚ 驱动程åºåº”通过以下API将性能域注册到EM框架ä¸:: int em_dev_register_perf_domain(struct device *dev, unsigned int nr_states, struct em_data_callback *cb, cpumask_t *cpus, bool microwatts); 驱动程åºå¿…é¡»æ供一个回调函数,为æ¯ä¸ªæ€§èƒ½çŠ¶æ€è¿”回<频率,功率>å…ƒç»„ã€‚é©±åŠ¨ç¨‹åº æ供的回调函数å¯ä»¥è‡ªç”±åœ°ä»Žä»»ä½•ç›¸å…³ä½ç½®ï¼ˆDTã€å›ºä»¶......)以åŠä»¥ä»»ä½•è¢«è®¤ä¸ºæ˜¯ å¿…è¦çš„æ–¹å¼èŽ·å–æ•°æ®ã€‚åªæœ‰å¯¹äºŽCPU设备,驱动程åºå¿…须使用cpumask指定性能域的CPU。 对于CPU以外的其他设备,最åŽä¸€ä¸ªå‚数必须被设置为NULL。 最åŽä¸€ä¸ªå‚数“microwattsâ€ï¼ˆå¾®ç“¦ï¼‰è®¾ç½®æˆæ£ç¡®çš„值是很é‡è¦çš„,使用EMçš„å†…æ ¸ å系统å¯èƒ½ä¼šä¾èµ–è¿™ä¸ªæ ‡å¿—æ¥æ£€æŸ¥æ‰€æœ‰çš„EM设备是å¦ä½¿ç”¨ç›¸åŒçš„刻度。如果有ä¸åŒçš„ 刻度,这些å系统å¯èƒ½å†³å®šè¿”回è¦å‘Š/错误,åœæ¢å·¥ä½œæˆ–崩溃(panic)。 关于实现这个回调函数的驱动程åºçš„例å,å‚è§ç¬¬3节。或者在第2.4节阅读这个API 的更多文档。 使用DTçš„EM注册 ============== EM也å¯ä»¥ä½¿ç”¨OPP框架和DT "æ“作点-v2 "ä¸çš„ä¿¡æ¯æ³¨å†Œã€‚DTä¸çš„æ¯ä¸ªOPPæ¡ç›®éƒ½å¯ 以用一个包å«å¾®ç“¦ç‰¹åŠŸçŽ‡å€¼çš„属性 "op-microwatt "æ¥æ‰©å±•ã€‚这个OPP DTå±žæ€§å… è®¸å¹³å°æ³¨å†Œåæ˜ æ€»åŠŸçŽ‡ï¼ˆé™æ€+动æ€ï¼‰çš„EM功率值。这些功率值å¯èƒ½ç›´æŽ¥æ¥è‡ªå®žéªŒå’Œ 测é‡ã€‚ “人工â€EM的注册 ============== 有一个选项å¯ä»¥ä¸ºç¼ºå°‘关于æ¯ä¸ªæ€§èƒ½çŠ¶æ€çš„功率值的详细知识的驱动程åºæ供一个自 定义回调。回调.get_cost()是å¯é€‰çš„,它æä¾›EAS使用的“æˆæœ¬â€å€¼ã€‚这对那些åªæ ä¾›CPU类型之间相对效率信æ¯çš„å¹³å°å¾ˆæœ‰ç”¨ï¼Œäººä»¬å¯ä»¥åˆ©ç”¨è¿™äº›ä¿¡æ¯æ¥åˆ›å»ºä¸€ä¸ªæŠ½è±¡çš„ 功率模型。但是,考虑到输入功率值的大å°é™åˆ¶ï¼Œå³ä½¿æ˜¯æŠ½è±¡çš„功率模型有时也很难装 进去。.get_cost()å…许æä¾›åæ˜ CPU效率的“æˆæœ¬â€å€¼ã€‚这将å…许æä¾›EASä¿¡æ¯ï¼Œå®ƒ 与EM内部计算'æˆæœ¬'值的公å¼æœ‰ä¸åŒçš„关系。è¦ä¸ºè¿™æ ·çš„å¹³å°æ³¨å†ŒEM,驱动程åºå¿…é¡» å°†æ ‡å¿—â€œmicrowattsâ€è®¾ç½®ä¸º0,æä¾›.get_power()回调和.get_cost()回调。EM 框架会在注册过程ä¸æ£ç¡®å¤„ç†è¿™æ ·çš„å¹³å°ã€‚è¿™ç§å¹³å°ä¼šè¢«è®¾ç½®EM_PERF_DOMAIN_ARTIFICIAL æ ‡å¿—ã€‚å…¶ä»–ä½¿ç”¨EM的框架应该特别注æ„测试和æ£ç¡®å¯¹å¾…è¿™ä¸ªæ ‡å¿—ã€‚ “简å•â€EM的注册 ~~~~~~~~~~~~~~~~ “简å•â€EM是用框架的辅助函数cpufreq_register_em_with_opp()注册的。它实现了 一个和以下数å¦å…¬å¼ç´§å¯†ç›¸å…³çš„功率模型:: Power = C * V^2 * f 使用这ç§æ–¹æ³•æ³¨å†Œçš„EMå¯èƒ½æ— 法æ£ç¡®åæ˜ çœŸå®žè®¾å¤‡çš„ç‰©ç†ç‰¹æ€§ï¼Œä¾‹å¦‚当é™æ€åŠŸçŽ‡ (æ¼ç”µæµåŠŸçŽ‡ï¼‰å¾ˆé‡è¦æ—¶ã€‚ 2.3 访问性能域 ^^^^^^^^^^^^^^ 有两个API函数æ供对能é‡æ¨¡åž‹çš„访问。em_cpu_get()以CPU id为å‚数,em_pd_get() 以设备指针为å‚数。使用哪个接å£å–决于å系统,但对于CPU设备æ¥è¯´ï¼Œè¿™ä¸¤ä¸ªå‡½æ•°éƒ½è¿” 回相åŒçš„性能域。 对CPU的能é‡æ¨¡åž‹æ„Ÿå…´è¶£çš„å系统å¯ä»¥é€šè¿‡em_cpu_get() API检索它。在创建性能域时 分é…一次能é‡æ¨¡åž‹è¡¨ï¼Œå®ƒä¿å˜åœ¨å†…å˜ä¸ä¸è¢«ä¿®æ”¹ã€‚ 一个性能域所消耗的能é‡å¯ä»¥ä½¿ç”¨em_cpu_energy() APIæ¥ä¼°ç®—。该估算å‡å®šCPU设备 使用的CPUfreq监管器是schedutil。当å‰è¯¥è®¡ç®—ä¸èƒ½æ供给其它类型的设备。 关于上述API的更多细节å¯ä»¥åœ¨ ``<linux/energy_model.h>`` 或第2.4节ä¸æ‰¾åˆ°ã€‚ 2.4 API的细节æè¿° ^^^^^^^^^^^^^^^^^ å‚è§ include/linux/energy_model.h å’Œ kernel/power/energy_model.c çš„kernel doc。 3. 驱动示例 ----------- CPUFreq框架支æŒä¸“用的回调函数,用于为指定的CPU(们)注册EM: cpufreq_driver::register_em()。这个回调必须为æ¯ä¸ªç‰¹å®šçš„驱动程åºæ£ç¡®å®žçŽ°ï¼Œ å› ä¸ºæ¡†æž¶ä¼šåœ¨è®¾ç½®è¿‡ç¨‹ä¸é€‚时地调用它。本节æ供了一个简å•çš„例å,展示CPUFreq驱动 在能é‡æ¨¡åž‹æ¡†æž¶ä¸ä½¿ç”¨ï¼ˆå‡çš„)“fooâ€å议注册性能域。该驱动实现了一个est_power() 函数æ供给EM框架:: -> drivers/cpufreq/foo_cpufreq.c 01 static int est_power(struct device *dev, unsigned long *mW, 02 unsigned long *KHz) 03 { 04 long freq, power; 05 06 /* 使用“fooâ€åè®®è®¾ç½®é¢‘çŽ‡ä¸Šé™ */ 07 freq = foo_get_freq_ceil(dev, *KHz); 08 if (freq < 0); 09 return freq; 10 11 /* 估算相关频率下设备的功率æˆæœ¬ */ 12 power = foo_estimate_power(dev, freq); 13 if (power < 0); 14 return power; 15 16 /* 将这些值返回给EM框架 */ 17 *mW = power; 18 *KHz = freq; 19 20 return 0; 21 } 22 23 static void foo_cpufreq_register_em(struct cpufreq_policy *policy) 24 { 25 struct em_data_callback em_cb = EM_DATA_CB(est_power); 26 struct device *cpu_dev; 27 int nr_opp; 28 29 cpu_dev = get_cpu_device(cpumask_first(policy->cpus)); 30 31 /* 查找该ç–略支æŒçš„OPPæ•°é‡ */ 32 nr_opp = foo_get_nr_opp(policy); 33 34 /* 并注册新的性能域 */ 35 em_dev_register_perf_domain(cpu_dev, nr_opp, &em_cb, policy->cpus, 36 true); 37 } 38 39 static struct cpufreq_driver foo_cpufreq_driver = { 40 .register_em = foo_cpufreq_register_em, 41 };