本文專門針對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(0?是默認設置)。當這個參數設置為?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。
?
評論
查看更多