1. 程式人生 > >linux CPU動態調頻【轉】

linux CPU動態調頻【轉】

轉自:https://www.xuebuyuan.com/2185926.html

針對sep4020的linux低功耗研究也有一段時間了,基本把低功耗的實現方式想清楚了(主要分成機制和策略),這段時間的工作主要在機制上。暫時想實現的主要的機制有:cpu級,裝置驅動級,系統平臺級。管理顆粒度不斷遞增,形成三駕馬車齊驅的形勢。

 

cpu級:主要實現比較容易的在系統處於目標在於頻繁發生、更高粒度的電源狀態改變,主要的實現方式為idle,包括今天的主要想講的動態主頻。

裝置驅動級:主要實現對單個裝置驅動的管理(suspend,resume等),通過系統監測將閒置的裝置,通過從使用者態對sys檔案目錄動態進行單個驅動裝置的管理

,置於省電模式。

系統平臺級:目標在於管理較大的、非常見的重大電源狀態改變,用於減少產品裝置在長時間的空閒之後,減少電源消耗。主要實現方式是依託linux核心所支援的apm技術,實現整個系統的睡眠/恢復(sleep)

 

這幾個層次其實並不是相互獨立的,都是相互交叉的,比如系統平臺級的睡眠不可避免會涉及到cpu的sleep模式和裝置驅動的掛起,而動態主頻的實現除了cpu本身的支援也需要外圍驅動隨著主頻變化做出相應的適應活動。因此這裡的分級只是一種粗範圍的,邏輯上的分層。

 

前段時間還調研了一下IBM和Monta Vista搞得那套DPM(Dynamic Power Management)機制,看了不少論文和觀點,總的感覺就是太過複雜而且也不是很實用,感覺噱頭大過實際功效,(因此這套機制始終還不能進入核心的mainline),言歸正傳,還是重點講述下cpufreq技術。

 

1.為什麼要cpufreq?

 

關於要不要實現cpufreq技術,我也糾結過,一個原因是:當時對核心如何提供這麼一套動態變頻的機制還不瞭解,只覺得應該非常麻煩,因為涉及到外圍驅動的引數更新,另外一個原因是:在SEP4020這種體量的處理器上跑linux,即使執行在最高頻率時的處理能力可能也不是很富餘,我再給它降頻還有沒有意義?掙扎之後還是覺得要實現它,我也給自己列了這麼幾條原因:

n      雖然cpu在板級中已不是主要的耗電源,但是仍然佔著舉足輕重的位置,功耗機制到最後就是幾毫安幾毫安的扣了,降頻肯定能在一定程式上節約功耗那我為什麼不採用?

n      細化功耗管理的顆粒度,為應用程式提供更多的功耗節省機制

n      對普通的應用,系統可以執行在維持平臺運作的最低頻率,在有處理任務時,變頻機制會自動切換到合適的高主頻,並且在任務結束時重回省電的低主頻,這樣就解決了我之前的第二個疑惑。

Ø        SEP4020在執行在88M時板級功耗為:222mA

Ø        SEP4020在執行在56M時板級功耗為:190mA,降低14%

Ø        SEP4020在執行在32M時板級功耗為:160mA,降低28%

n      實現的一些工作是我們一直需要去做但是一直沒有動力做的

Ø        變頻會涉及到大量模組的引數的重新配置,作為cpu原廠,我們需要把這些引數徹底掌握

Ø        對這些引數的充分理解,能對現有系統進行優化,提升整體系統的效率,比如使用發現一些引數還是太過保守(sdram,nand),我們的通用配置在系統降為32M時仍能正常工作。

n      可行性論證沒有問題:偶然看到armkiller同志提供的nand驅動程式碼中有變頻的實現(這裡非常感謝armkiller),網上這方面的文章很少,於是翻閱了linux核心原始碼中自帶的/documentation/cpufreq後,對這種機制大概有一定的瞭解(linux中的documentation是個好東東),也看到了一些處理器廠商為自己的cpu已經實現了的程式碼,如sa1100,pxa系列。

 

2. 核心所提供的這種cpufreq技術的機制

n      目的:

變頻技術是指CPU硬體本身支援在不同的頻率下執行,系統在執行過程中可以根據隨時可能發生變化的系統負載情況動態在這些不同的執行頻率之間進行切換,從而達到對效能和功耗做到二者兼顧的目的。

n      來源:

雖然多個處理器生產廠家都提供了對變頻技術的支援,但是其硬體實現和使用方法必然存在著細微甚至巨大的差別。這就使得每個處理器生產廠家都需要按照其特殊的硬體實現和使用方法向核心中新增程式碼,從而讓自己產品中的變頻技術在Linux
中得到支援和使用。然而,這種核心開發模式所導致的後果是各個廠家的實現程式碼散落在 Linux
核心程式碼樹的各個角落裡,各種不同的實現之間沒有任何程式碼是共享的,這給核心的維護以及將來新增對新的產品的支援都帶來了巨大的開銷,並直接導致了 cpufreq
核心子系統的誕生。

 

n      管理策略:

Linux內部共有五種對頻率的管理策略userspace,conservative,ondemand,powersave
和 performance

Ø         1.performance :CPU會固定工作在其支援的最高執行頻率上;

Ø         2.powersave :CPU會固定工作在其支援的最低執行頻率上。因此這兩種 governors
都屬於靜態 governor ,即在使用它們時 CPU
的執行頻率不會根據系統執行時負載的變化動態作出調整。這兩種 governors
對應的是兩種極端的應用場景,使用 performance governor
體現的是對系統高效能的最大追求,而使用 powersave governor
則是對系統低功耗的最大追求。

Ø        3.Userspace:最早的 cpufreq
子系統通過 userspace governor 
為使用者提供了這種靈活性。系統將變頻策略的決策權交給了使用者態應用程式,並提供了相應的介面供使用者態應用程式調節CPU
執行頻率使用。(可以使用Dominik 等人開發了 cpufrequtils
工具包)

Ø        4.ondemand :userspace是核心態的檢測,效率低。而ondemand正是人們長期以來希望看到的一個完全在核心態下工作並且能夠以更加細粒度的時間間隔對系統負載情況進行取樣分析的
governor。

Ø        5.conservative : ondemand governor
的最初實現是在可選的頻率範圍內調低至下一個可用頻率。這種降頻策略的主導思想是儘量減小對系統性能的負面影響,從而不會使得系統性能在短時間內迅速降低以影響使用者體驗。但是在 ondemand governor
的這種最初實現版本在社群釋出後,大量使用者的使用結果表明這種擔心實際上是多餘的, ondemand governor
在降頻時對於目標頻率的選擇完全可以更加激進。因此最新的 ondemand governor
在降頻時會在所有可選頻率中一次性選擇出可以保證 CPU 
工作在 80% 以上負荷的頻率,當然如果沒有任何一個可選頻率滿足要求的話則會選擇 CPU
支援的最低執行頻率。大量使用者的測試結果表明這種新的演算法可以在不影響系統性能的前提下做到更高效的節能。在演算法改進後, ondemand governor
的名字並沒有改變,而 ondemand governor 
最初的實現也儲存了下來,並且由於其演算法的保守性而得名 conservative

Ondemand降頻更加激進,conservative降頻比較緩慢保守,事實使用ondemand的效果也是比較好的。

 

n      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(0
是預設設定)。當這個引數設定為 1 時,任何具有 “nice”
值的處理器不計入總處理器利用率。在設定為 0 時,所有處理器都計入利用率。

Ø        sampling_down_factor:

 

n      使用方法:

Ø        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

 

3.如何實現?

       首先需要幹一些雜活,修改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

 

4.效果

在sys下開啟ondeman模式,串上電流表:

1.      板級電流從220mA調至160mA(因為此時核心檢測系統無負載,降頻)

2.      執行一個nandflash的拷貝命令,拷貝一個5M左右的檔案到其他資料夾,

3.      在拷貝執行時間在3秒時(我給核心設的掃描週期為2.5秒)系統發現有負載,升頻,電流從160mA變為220mA(可見已是系統最高主頻)

4.      此後的拷貝的整個過程中電流保持為220mA

5.      在拷貝結束後不久(2-3s內),系統電流又跳變至160mA。