CPUFreq驅動

来源:https://www.cnblogs.com/linhaostudy/archive/2018/07/23/9355595.html
-Advertisement-
Play Games

CPUFreq子系統位於 drivers/cpufreq目錄下,負責進行運行過程中CPU頻率和電壓的動態調整,即DvFS( Dynamic Voltage Frequency Scaling,動態電壓頻率調整)。運行時進行CPU電壓和頻率調整的原因是:CMOS電路中的功耗與電壓的平方成正比、與頻率成 ...


CPUFreq子系統位於 drivers/cpufreq目錄下,負責進行運行過程中CPU頻率和電壓的動態調整,即DvFS( Dynamic Voltage Frequency Scaling,動態電壓頻率調整)。運行時進行CPU電壓和頻率調整的原因是:CMOS電路中的功耗與電壓的平方成正比、與頻率成正比(P∝fV2)因此降低電壓和頻率可降低功耗。
CPUFreq的核心層位於drivers/cpufreq/cpufreq,c下,它為各個SoC的CPUFreq驅動的實現提供了一套統一的介面,並實現了一套notifier機制,可以在 CPUFreq的策略和頻率改變的時候向其他模塊發出通知。另外,在CPU運行頻率發生變化的時候,內核的 loops perify常數也會發生相應變化。

SOC的CPUFreq驅動實現

每個SoC的具體CPUFreq驅動實例只需要實現電壓、頻率表,以及從硬體層面完成這些變化。
CPUFreq核心層提供瞭如下API以供SoC註冊自身的CPUFreq驅動:
int cpufreq_register_driver(struct cpufreq_driver *driver_data)
其參數為一個cpufreq_driver結構體指針,實際上,cpufreq_driver封裝了一個具體的SoC的CPUFreq驅動的主體,該結構體形如代碼如下所示。

struct cpufreq_driver {
    char            name[CPUFREQ_NAME_LEN];
    u8          flags;

    /* needed by all drivers */
    int (*init)     (struct cpufreq_policy *policy);
    int (*verify)   (struct cpufreq_policy *policy);

    /* define one out of two */
    int (*setpolicy)    (struct cpufreq_policy *policy);
    int (*target)   (struct cpufreq_policy *policy, /* Deprecated */
                 unsigned int target_freq,
                 unsigned int relation);
    int (*target_index) (struct cpufreq_policy *policy,
                 unsigned int index);

    /* should be defined, if possible */
    unsigned int    (*get)  (unsigned int cpu);

    /* optional */
    int (*bios_limit)   (int cpu, unsigned int *limit);

    int (*exit)     (struct cpufreq_policy *policy);
    int (*suspend)  (struct cpufreq_policy *policy);
    int (*resume)   (struct cpufreq_policy *policy);
    struct freq_attr    **attr;
};

其中的 owner成員一般被設置為 THIS MODULE;name成員是CPUFreq驅動的名字,如drivers/cpufreq/s5pv210-cpufreq.c設置name為s5pv210, drivers/cpufreq/omap-cpufreq.c設置name為omap;falgs是一些暗示性的標誌,譬如,若設置了 CPUFREQ_CONST_LOOPS,則是告訴內核loops_per_jiffy不會因為CPU頻率的變化而變化。
init()成員是一個per-CPU初始化函數指針,每當一個新的CPU被註冊進系統的時候,該函數就被調用,該函數接受一個cpufreq_policy的指針參數,在init()成員函數中,可進行如下設置:

policy->cpuinfo.min_freq
policy->cpuinfo.max_freq

上述代碼描述的是該CPU支持的最小頻率和最大頻率(單位是kHz)。

policy->cur

上述代碼描述的是CPU的當前頻率;

policy->policy
policy->governor
policy->min
policy->max

上述代碼定義該CPU的預設策略,以及在預設策略情況下,該策略支持的最小、最大CPU頻率。
verify成員函數用於對用戶的 CPUFreq策略設置進行有效性驗證和數據修正。每當用戶設定一個新策略時,該函數根據老的策略和新的策略,檢驗新策略設置的有效性並對無效設置進行必要的修正。在該成員函數的具體實現中,常用到如下輔助函數:

static inline void
cpufreq_verify_within_cpu_limits(struct cpufreq_policy *policy)
{
    cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
    policy->cpuinfo.max_freq);
}

setpolicyo成員函數接受一個policy參數(包含policy->policypolicy->minpolicy->max等成員),實現了這個成員函數的CPU一般具備在一個範圍(limit,從policy->minpolicy->max)里自動調整頻率的能力。目前只有少數驅動(如intel_pstate.c和longrun.c)包含這樣的成員函數,而絕大多數CPU都不會實現此函數,一般只實現target()成員函數,target()的參數直接就是一個指定的頻率。
target()成員函數用於將頻率調整到一個指定的值,接受3個參數: policy、 target_freq和relation, target freq是目標頻率,實際驅動總是要設定真實的CPU頻率到最接近於 target_feq,並且設定的頻率必須位於 policy->min到 policy->max之間。在設定頻率接近 target_feq的情況下, relation若為 CPUFREQ REL I,則暗示設置的頻率應該大於或等於 target_freq; relation若為 CPUFREQ_REL_H,則暗示設置的頻率應該小於或等於 target_freq。
表19.1描述了 setpolicy()和 target()所針對的CPU以及調用方式上的區別。

setpolicy() target()
CPU有在一定範圍內獨立調整頻率的能力 CPU只能指定頻率
CPU freq policy 調用到setpolicy(),由CPU獨立在一個範圍內調整頻率 由CPU Freq核心層根據系統負載和策略綜合決定目標頻率

根據晶元內部PLL和分頻器的關係, ARM SOC一般不具備獨立調整頻率的能力,往往SoC的 CPUFreq驅動會提供一個頻率表,頻率在該表的範圍內進行變更,因此一般實現target()成員函數。
CPUFreq核心層提供了一組與頻率表相關的輔助API。

int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy,
                    struct cpufreq_frequency_table *table)
{
    unsigned int min_freq = ~0;
    unsigned int max_freq = 0;
    unsigned int i;

    for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
        unsigned int freq = table[i].frequency;
        if (freq == CPUFREQ_ENTRY_INVALID) {
            pr_debug("table entry %u is invalid, skipping\n", i);

            continue;
        }
        pr_debug("table entry %u: %u kHz, %u driver_data\n",
                    i, freq, table[i].driver_data);
        if (freq < min_freq)
            min_freq = freq;
        if (freq > max_freq)
            max_freq = freq;
    }

    policy->min = policy->cpuinfo.min_freq = min_freq;
    policy->max = policy->cpuinfo.max_freq = max_freq;

    if (policy->min == ~0)
        return -EINVAL;
    else
        return 0;
}

它是 cpufreq driver的init成員函數的助手,用於將policy->min和 policy->max設置為與 cpuinfo->min_freq和 cpuinfo.max_freq相同的值。

int cpufreq_frequency_table_verify(struct cpufreq_policy *policy,
                   struct cpufreq_frequency_table *table)

它是 cpufreq driver的verify成員函數的助手,確保至少有1個有效的CPU頻率位於policy->min到 policy->max的範圍內。

int cpufreq_frequency_table_target(struct cpufreq_policy *policy,
                   struct cpufreq_frequency_table *table,
                   unsigned int target_freq,
                   unsigned int relation,
                   unsigned int *index)

CPUFreq的策略

SoCCPUFreq驅動只是設定了CPU的頻率參數,以及提供了設置頻率的途徑,但是它並不會管CPU自身究竟應該運行在哪種頻率上。究竟頻率依據的是哪種標準,進行何種變化而這些完全由 CPUFreq的策略( policy)決定,這些策略如表19.2所示。

CPUFreq的策略 策略實現的方式
cpufreq_ondemand 平時以低速的方式運行,當系統負載提高需自動提高頻率
cpufreq_performance cpu以最高頻率運行,即scaling_max_freq
cpufreq_consevative 字面含義是傳統的、保守的,跟ondemand相似,區別在於動態頻率在變更的時候採用漸進的方式
cpufreq_powersave cpu以最低頻率運行,即scaling_min_freq
cpufreq_userspace 讓根用戶通過sys節點scaling_setspeed設置頻率

在 Android系統中,則增加了1個交互策略,該策略適合於對延遲敏感的U1交互任務,當有UI交互任務的時候,該策略會更加激進並及時地調整CPU頻率。
總而言之,系統的狀態以及CPUFreq的策略共同決定了CPU頻率跳變的目標, CPUFreq核心層並將目標頻率傳遞給底層具體SoC的 CPUFreq驅動,該驅動修改硬體,完成頻率的變換,如圖19.2所示。

用戶空間一般可通過 /sys/devices/system/cpu/cpux/cpufreq節點來設置 CPUFreq。譬如,我們要設置 CPUFreq到700Mhz,採用 userspace策略,則運行如下命令:

echo userspace >/sys/devices/system/cpu/cpu0/cpufreq/scaling governor
echo 700000>/sys/devices/system/cpu/cpu/cpufreq/scaling set speed

CPUFreq的性能測試和調優

使用cpufreq-bench工具可以幫助工程師分析採用CPUFreq後對系統性能的影響;

CPUFreq通知

CPUFreq子系統會發出通知的情況有兩種:CPUFreq的策略變化或者CPU運行頻率變化。
在策略變化的過程中,會發送3次通知:

  • CPUFREQ ADJUST:所有註冊的 notifier可以根據硬體或者溫度的情況去修改範圍(即 policy->min和 policy->max);
  • CPUFREQ INCOMPATIBLE:除非前面的策略設定可能會導致硬體出錯,否則被註冊的notifier不能改變範圍等設定;
  • CPUFREQ NOTIFY:所有註冊的notifier都會被告知新的策略已經被設置。在頻率變化的過程中,會發送2次通知;
  • CPUFREQ PRECHANGE:準備進行頻率變更;
  • CPUFREQ POSTCHANGE:已經完成頻率變更。

notifier中的第3個參數是一個 cpufreq_freqs的結構體,包含cpu(CPU號)、old(過去的頻率)和new(現在的頻率)這3個成員。發送 CPUFREQ_PRECHANGE和 CPUFREQ_POSTCHANGE的代碼如下:

int srcu_notifier_call_chain(struct srcu_notifier_head *nh,
        unsigned long val, void *v)
    

如果某模塊關心 CPUFREQ_PRECHANGE或 CPUFREQ_POSTCHANGE事件,可簡單地使用 Linux notifier機制監控。譬如, drivers/video/sallo0fbc在CPU頻率變化過程中需對自身硬體進行相關設置,因此它註冊了 notifier併在 CPUFREQ _PRECHANGE和CPUFREQ_POSTCHANGE情況下分別進行不同的處理,如代碼清單19.3所示。

#ifdef CONFIG_CPU_FREQ
    fbi->freq_transition.notifier_call = sa1100fb_freq_transition;
    fbi->freq_policy.notifier_call = sa1100fb_freq_policy;
    cpufreq_register_notifier(&fbi->freq_transition, CPUFREQ_TRANSITION_NOTIFIER);
    cpufreq_register_notifier(&fbi->freq_policy, CPUFREQ_POLICY_NOTIFIER);
#endif


/*
 * CPU clock speed change handler.  We need to adjust the LCD timing
 * parameters when the CPU clock is adjusted by the power management
 * subsystem.
 */
static int
sa1100fb_freq_transition(struct notifier_block *nb, unsigned long val,
             void *data)
{
    struct sa1100fb_info *fbi = TO_INF(nb, freq_transition);
    struct cpufreq_freqs *f = data;
    u_int pcd;

    switch (val) {
    case CPUFREQ_PRECHANGE:
        set_ctrlr_state(fbi, C_DISABLE_CLKCHANGE);
        break;

    case CPUFREQ_POSTCHANGE:
        pcd = get_pcd(fbi->fb.var.pixclock, f->new);
        fbi->reg_lccr3 = (fbi->reg_lccr3 & ~0xff) | LCCR3_PixClkDiv(pcd);
        set_ctrlr_state(fbi, C_ENABLE_CLKCHANGE);
        break;
    }
    return 0;
}

此外,如果在系統掛起/恢復的過程中CPU頻率會發生變化,則 CPUFreq子系統也會發出CPUFREQ_SUSPENDCHANGE和 CPUFREQ _RESUMECHANGE這兩個通知。

值得一提的是,除了CPU以外,一些非CPU設備也支持多個操作頻率和電壓,存在多個OPP。Linux3.2之後的內核也支持針對這種非CPU設備的DVFS,該套子系統為Devfreq。與CPUFreq存在一個drivers/cpufreq目錄相似,在內核中也存在一個drivers/devore的目錄。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 決定認真從零開始寫一個Linux的學習過程,像我這麼偷懶的人能寫文字記錄已經很不容易了,希望不要半途而廢吧(拖走) 用多了Linux其實發現,要是哪天Linux和Windows能結合下就好了,簡單粗暴的Linux命令行加上Windows萬能的UI以及相容才是夢想中的系統啊,瓶瓶罐罐的東西真是討厭~~ ...
  • 基於Red Hat Enterprise Linux 7.5 Linux中的root就是存在於所有類UNIX系統中的超級用戶,持有最高管理許可權,能添加/刪除用戶、開關機、關閉或開啟硬體或者系統服務等,並且root的操作無法被阻擋。 Linux中root為什麼叫root,不是因為名字叫root,而是U ...
  • 我的 Ubuntu 鏡像是在「清華大學開源軟體鏡像網」下載的,版本號是16.04 下載一個ios文件,裝在虛擬機中就行了。 虛擬機用的 VMware Workstation 12, 註冊密鑰: 5A02H-AU243-TZJ49-GTC7K-3C61N AA39H-61W50-H8DCY-1MM79 ...
  • 什麼是netstat     在Linux系統中輸入 man netstat ,顯示的結果如下所示:     從上面可以看出netstat命令的主要功能為: 顯示網路連接信息 顯示路由表信息 顯示網卡統計信息 顯示無效的連接信息 顯示組播成員信息   ...
  • df命令用於查看磁碟的分區,磁碟已使用的空間,剩餘的空間 1、用法 df [選項] [文件..] 2、命令選項 -a,--all 全部文件系統-h,--human-readable 以以合適的單位來顯示信息-H,--si 與-h參數相同,但在計算時是以1000 Bytes為換算單位而非1024 By ...
  • 配置號IP地址後,使用ifconfig命令,沒有顯示eth0,重啟網卡(service network restart)又遇到以下報錯信息 報錯信息: 解決辦法: 首先使用ifconfig -a命令,記錄下eth1的mac地址:00:0C:29:C5:AA:CF 然後打開/etc/sysconfig ...
  • 卸載已經安裝的Docker 添加阿裡鏡像 查看可以安裝的docker版本 安裝18.03.1 啟動 Docker 設置Docker阿裡雲加速器 sudo mkdir p /etc/docker sudo tee /etc/docker/daemon.json 註意:需要替換 為你的阿裡雲加速器地址, ...
  • chmod用於管理文件或目錄的許可權,文件或目錄許可權的控制分別以讀取(r)、寫入(w)、執行(x)3種 可讀可寫可執行,抽象的用二進位來表示 1 代表擁有該許可權,0 代表沒有該許可權,這樣我們就可以看到 具有全部許可權二進位可理解為 “111” 即 十進位的 “7”,只有讀寫許可權二進位可理解為 “100” ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...