日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > linux >内容正文

linux

Linux cpuidle framework(4)_menu governor

發(fā)布時(shí)間:2025/3/21 linux 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux cpuidle framework(4)_menu governor 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Linux cpuidle framework(4)_menu governor

menu governor的主要任務(wù)就轉(zhuǎn)化為兩個(gè):1. 根據(jù)系統(tǒng)的運(yùn)行情況,預(yù)測(cè)CPU將在C state中停留的時(shí)間(簡(jiǎn)稱predicted_us);2. 借助pm qos framework,獲取系統(tǒng)當(dāng)前的延遲容忍度(簡(jiǎn)稱latency_req)。

1. 前言

本文以menu governor為例,進(jìn)一步理解cpuidle framework中g(shù)overnor的概念,并學(xué)習(xí)governor的實(shí)現(xiàn)方法。

在當(dāng)前的kernel中,有2個(gè)governor,分別為ladder和menu(蝸蝸試圖理解和查找,為什么會(huì)叫這兩個(gè)名字,暫時(shí)還沒(méi)有答案)。ladder在periodic timer tick system中使用,menu在tickless system中使用。

現(xiàn)在主流的系統(tǒng),出于電源管理的考量,大多都是tickless system。另外,menu governor會(huì)利用pm qos framework(蝸蝸會(huì)在后續(xù)的文章中分析),在選擇策略中加入延遲容忍度(Latency tolerance)的考量。因此本文選取menu governor作為分析對(duì)象,至于ladder,就不再分析了。

注:有關(guān)periodic timer tick和tickless的知識(shí),可參考本站時(shí)間子系統(tǒng)的系列文章。

2. 背后的思考

本節(jié)的內(nèi)容,主要來(lái)源于drivers/cpuidle/governors/menu.c中的注釋。

governor的主要職責(zé),是根據(jù)系統(tǒng)的運(yùn)行情況,選擇一個(gè)合適idle state(在kernel的標(biāo)準(zhǔn)術(shù)語(yǔ)中,也稱作C state)。具體的算法,需要基于下面兩點(diǎn)考慮:

1)切換的代價(jià)

進(jìn)入C state的目的,是節(jié)省功耗,但CPU在C state和normal state之間切換,是要付出功耗上面的代價(jià)的。這最終會(huì)體現(xiàn)在idle state的target_residency字段上。

idle driver在注冊(cè)idle state時(shí),要非常明確state切換的代價(jià),基于該代價(jià),CPU必須在idle state中停留超過(guò)一定的時(shí)間(target_residency)才是劃算的。

因此governor在選擇C state時(shí),需要預(yù)測(cè)出CPU將要在C state中的停留時(shí)間,并和備選idle state的target_residency字段比較,選取滿足“停留時(shí)間 > target_residency”的state。

2)系統(tǒng)的延遲容忍程度

備選的的C state中,功耗和退出延遲是一對(duì)不可調(diào)和的矛盾,電源管理的目標(biāo),是在保證延遲在系統(tǒng)可接受的范圍內(nèi)的情況下,盡可能的節(jié)省功耗。

idle driver在注冊(cè)idle state時(shí),會(huì)提供兩個(gè)信息:CPU在某個(gè)state下的功耗(power_usage)和退出該state的延遲(exit_latency)。那么如果知道系統(tǒng)當(dāng)前所能容忍的延遲(簡(jiǎn)稱latency_req),就可以在所有exit_latency小于latency_req的state中,選取功耗最小的那個(gè)。

因此,governor算法就轉(zhuǎn)換為獲取系統(tǒng)當(dāng)前的latency_req,而這正是pm qos的特長(zhǎng)。

基于上面的考量,menu governor的主要任務(wù)就轉(zhuǎn)化為兩個(gè):1. 根據(jù)系統(tǒng)的運(yùn)行情況,預(yù)測(cè)CPU將在C state中停留的時(shí)間(簡(jiǎn)稱predicted_us);2. 借助pm qos framework,獲取系統(tǒng)當(dāng)前的延遲容忍度(簡(jiǎn)稱latency_req)。

任務(wù)1,menu governor從如下幾個(gè)方面去達(dá)成:

前面講過(guò),menu governor用于tickless system,簡(jiǎn)化處理,menu將“距離下一個(gè)tick來(lái)臨的時(shí)間(由next timer event測(cè)量,簡(jiǎn)稱next_timer_us)”作為基礎(chǔ)的predicted_us。

當(dāng)然,這個(gè)基礎(chǔ)的predicted_us是不準(zhǔn)確的,因?yàn)樵谶@段時(shí)間內(nèi),隨時(shí)都可能產(chǎn)生除next timer event之外的其它wakeup event。為了使預(yù)測(cè)更準(zhǔn)確,有必要加入一個(gè)校正因子(correction factor),該校正因子基于過(guò)去的實(shí)際predicted_us和next_timer_us之間的比率,例如,如果wakeup event都是在預(yù)測(cè)的next timer event時(shí)間的一半時(shí)產(chǎn)生,則factor為0.5。另外,為了更精確,menu使用動(dòng)態(tài)平均的factor。

另外,對(duì)不同范圍的next_timer_us,correction factor的影響程度是不一樣的。例如期望50ms和500ms的next timer event時(shí),都是在10ms時(shí)產(chǎn)生了wakeup event,顯然對(duì)500ms的影響比較大。如果計(jì)算平均值時(shí)將它們混在一起,就會(huì)對(duì)預(yù)測(cè)的準(zhǔn)確性產(chǎn)生影響,所以計(jì)算correction factor的數(shù)據(jù)時(shí),需要區(qū)分不同級(jí)別的next_timer_us。同時(shí),系統(tǒng)是否存在io wait,對(duì)factor的敏感度也不同。基于這些考慮,menu使用了一組factor(12個(gè)),分別用于不同next_timer_us、不同io wait的場(chǎng)景下的的校正。

最后,在有些場(chǎng)合下,next_timer_us的預(yù)測(cè)是完全不正確的,如存在固定周期的中斷時(shí)(音頻等)。這時(shí)menu采用另一種不同的預(yù)測(cè)方式:統(tǒng)計(jì)過(guò)去8次停留時(shí)間的標(biāo)準(zhǔn)差(stand deviation),如果小于一定的門限值,則使用這8個(gè)停留時(shí)間的平均值,作為預(yù)測(cè)值。

任務(wù)2,延遲容忍度(latency_req)的估算,menu綜合考慮了兩種因素,如下:

1)由pm qos獲得的,系統(tǒng)期望的,CPU和DMA的延遲需求。這是一個(gè)硬性指標(biāo)。

2)基于這樣一個(gè)經(jīng)驗(yàn)法則:越忙的系統(tǒng),對(duì)系統(tǒng)延遲的要求越高,結(jié)合任務(wù)1中預(yù)測(cè)到的停留時(shí)間(predicted_us),以及當(dāng)前系統(tǒng)的CPU平均負(fù)荷和iowaiters的個(gè)數(shù)(get_iowait_load函數(shù)獲得),算出另一個(gè)延遲容忍度,計(jì)算公式(這是一個(gè)經(jīng)驗(yàn)公式)為:?
??????????????? predicted_us / (1 + 2 * loadavg +10 * iowaiters)?
這個(gè)公式反映的是退出延遲和預(yù)期停留時(shí)間之間的比例,loadavg和iowaiters越大,對(duì)退出延遲的要求就越高奧。

最后,latency_req的值取上面兩個(gè)估值的最小值。

3. 代碼分析

理解menu governor背后的思考之后,再去看代碼,就比較簡(jiǎn)單了。

3.1 初始化

首先,在init代碼中,調(diào)用cpuidle_register_governor,注冊(cè)menu_governor,如下:

1: static struct cpuidle_governor menu_governor = { 2: .name = "menu", 3: .rating = 20, 4: .enable = menu_enable_device, 5: .select = menu_select, 6: .reflect = menu_reflect, 7: .owner = THIS_MODULE, 8: }; 9:? 10: /** 11: * init_menu - initializes the governor 12: */ 13: static int __init init_menu(void) 14: { 15: return cpuidle_register_governor(&menu_governor); 16: } 17:? 18: postcore_initcall(init_menu);

由menu_governor變量可知,該governor的名字為“menu”,rating為20,共提供了enable、select、reflect三個(gè)API。

3.2 enable API

enable API負(fù)責(zé)governor運(yùn)行前的準(zhǔn)備動(dòng)作,由menu_enable_device實(shí)現(xiàn):

1: static int menu_enable_device(struct cpuidle_driver *drv, 2: struct cpuidle_device *dev) 3: { 4: struct menu_device *data = &per_cpu(menu_devices, dev->cpu); 5: int i; 6:? 7: memset(data, 0, sizeof(struct menu_device)); 8:? 9: /* 10: * if the correction factor is 0 (eg first time init or cpu hotplug 11: * etc), we actually want to start out with a unity factor. 12: */ 13: for(i = 0; i < BUCKETS; i++) 14: data->correction_factor[i] = RESOLUTION * DECAY; 15:? 16: return 0; 17: }

由代碼可知,主要任務(wù)是初始化在私有數(shù)據(jù)結(jié)構(gòu)(struct menu_device)中保存的correction_factor。struct menu_device的定義如下:

1: struct menu_device { 2: int last_state_idx; 3: int needs_update; 4:? 5: unsigned int next_timer_us; 6: unsigned int predicted_us; 7: unsigned int bucket; 8: unsigned int correction_factor[BUCKETS]; 9: unsigned int intervals[INTERVALS]; 10: int interval_ptr; 11: };

last_state_idx,記錄了上一次進(jìn)入的C state;

needs_update,每次從C state返回時(shí),kernel(kernel\sched\idle.c)會(huì)調(diào)用governor的reflect接口,以便有機(jī)會(huì)讓governor考慮這一次state切換的結(jié)果(如更新統(tǒng)計(jì)信息)。對(duì)menu而言,它的reflect接口會(huì)設(shè)置needs_update標(biāo)志,并在下一次select時(shí),更新?tīng)顟B(tài),具體行為可參考后面的描述;

next_timer_us、predicted_us,可參考第2章中的有關(guān)說(shuō)明;

correction_factor,保存校正因子的數(shù)組,因子的個(gè)數(shù)為BUCKETS(當(dāng)前代碼為12);

bucket,指明select state時(shí)所使用的因子(當(dāng)前的校正因子);

intervals、interval_ptr,可參考第2章中的描述,用于計(jì)算停留時(shí)間的標(biāo)準(zhǔn)差,當(dāng)前代碼使用了8個(gè)停留時(shí)間(INTERVALS)。

3.2 select接口

governor的核心API,根據(jù)系統(tǒng)的運(yùn)行情況,選擇一個(gè)合適的C state。由menu_select接口實(shí)現(xiàn),邏輯如下:

1: /** 2: * menu_select - selects the next idle state to enter 3: * @drv: cpuidle driver containing state data 4: * @dev: the CPU 5: */ 6: static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) 7: { 8: struct menu_device *data = this_cpu_ptr(&menu_devices); 9: int latency_req = pm_qos_request(PM_QOS_CPU_DMA_LATENCY); 10: int i; 11: unsigned int interactivity_req; 12: unsigned long nr_iowaiters, cpu_load; 13:? 14: if (data->needs_update) { 15: menu_update(drv, dev); 16: data->needs_update = 0; 17: } 18:? 19: data->last_state_idx = CPUIDLE_DRIVER_STATE_START - 1; 20:? 21: /* Special case when user has set very strict latency requirement */ 22: if (unlikely(latency_req == 0)) 23: return 0; 24:? 25: /* determine the expected residency time, round up */ 26: data->next_timer_us = ktime_to_us(tick_nohz_get_sleep_length()); 27:? 28: get_iowait_load(&nr_iowaiters, &cpu_load); 29: data->bucket = which_bucket(data->next_timer_us, nr_iowaiters); 30:? 31: /* 32: * Force the result of multiplication to be 64 bits even if both 33: * operands are 32 bits. 34: * Make sure to round up for half microseconds. 35: */ 36: data->predicted_us = div_round64((uint64_t)data->next_timer_us * 37: data->correction_factor[data->bucket], 38: RESOLUTION * DECAY); 39:? 40: get_typical_interval(data); 41:? 42: /* 43: * Performance multiplier defines a minimum predicted idle 44: * duration / latency ratio. Adjust the latency limit if 45: * necessary. 46: */ 47: interactivity_req = data->predicted_us / performance_multiplier(nr_iowaiters, cpu_load); 48: if (latency_req > interactivity_req) 49: latency_req = interactivity_req; 50:? 51: /* 52: * We want to default to C1 (hlt), not to busy polling 53: * unless the timer is happening really really soon. 54: */ 55: if (data->next_timer_us > 5 && 56: !drv->states[CPUIDLE_DRIVER_STATE_START].disabled && 57: dev->states_usage[CPUIDLE_DRIVER_STATE_START].disable == 0) 58: data->last_state_idx = CPUIDLE_DRIVER_STATE_START; 59:? 60: /* 61: * Find the idle state with the lowest power while satisfying 62: * our constraints. 63: */ 64: for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++) { 65: struct cpuidle_state *s = &drv->states[i]; 66: struct cpuidle_state_usage *su = &dev->states_usage[i]; 67:? 68: if (s->disabled || su->disable) 69: continue; 70: if (s->target_residency > data->predicted_us) 71: continue; 72: if (s->exit_latency > latency_req) 73: continue; 74:? 75: data->last_state_idx = i; 76: } 77:? 78: return data->last_state_idx; 79: }

8行,取出per cpu的struct menu_device指針;

9行,調(diào)用pm_qos_request接口,獲取系統(tǒng)CPU和DMA所能容忍的延遲。因?yàn)閏puidle狀態(tài)下,運(yùn)行任何的中斷事件喚醒,因此這里只考慮了CPU和DMA;

14~17行,根據(jù)needs_update標(biāo)志,調(diào)用menu_update,更新統(tǒng)計(jì)信息,具體可參考代碼;

19行,last_state_idx會(huì)在menu_reflect中設(shè)置,并在menu_update中使用,此時(shí)已經(jīng)沒(méi)有用處了,初始化為無(wú)效值;

22~23行,如果pm qos要求的latency為0,則當(dāng)前系統(tǒng)是一個(gè)比較苛刻的狀態(tài),不能進(jìn)入idle狀態(tài),直接返回零。由此可以看出,software可以通過(guò)pm qos,控制系統(tǒng)是否可以進(jìn)入idle狀態(tài),后續(xù)分析pm qos時(shí),會(huì)再說(shuō)明;

26~29行,調(diào)用timer子系統(tǒng)的接口,獲取next_timer_us,調(diào)用sched提供de接口,獲取iowaiter的個(gè)數(shù)以及CPU load信息,并利用next_timer_us和iowaiters信息,計(jì)算出需要使用哪一類校正因子。計(jì)算邏輯比較簡(jiǎn)單,詳見(jiàn)代碼;

36~39行,將next_timer_us乘以校正因子,得到predicted_us。計(jì)算時(shí)考慮了溢出、精度等情況;

40行,調(diào)用get_typical_interval接口,檢查是否存在固定周期的情況,檢查的邏輯就是計(jì)算8次停留時(shí)間的標(biāo)準(zhǔn)差,如果存在,則利用平均值更新predicted_us;

42~48,根據(jù)predicted_us和系統(tǒng)負(fù)荷情況(cpu load、iowaiters),估算另一個(gè)延遲容忍值,并和latency_req,取最小值;

51~78行,根據(jù)上面的信息,查找cpuidle device的所有state,選出一個(gè)符合條件的state,并返回該state在cpuidle state數(shù)組中的index。

3.3 reflect接口

menu的reflect接口比較簡(jiǎn)單,更新data->last_state_idx后,置位data->needs_update標(biāo)志。可以多思考一下:為什么不直接在reflect中更新?tīng)顟B(tài),而是到下一次select時(shí)再更新?這個(gè)問(wèn)題留給讀者吧。

總結(jié)

以上是生活随笔為你收集整理的Linux cpuidle framework(4)_menu governor的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。